home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / adrbklib.c < prev    next >
C/C++ Source or Header  |  1997-02-24  |  149KB  |  5,671 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: adrbklib.c,v 4.153 1996/06/19 21:32:41 hubert Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.             T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade and Mike Seibel
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    Administration Builiding, AG-44
  13.    Seattle, Washington, 98195, USA
  14.    Internet: lgl@CAC.Washington.EDU
  15.              mikes@CAC.Washington.EDU
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *             Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *             Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.   ----------------------------------------------------------------------*/
  41.  
  42. #include "headers.h"
  43. #include "adrbklib.h"
  44.  
  45. #if defined(DOS) || defined(OS2)
  46. #define    ADRBK_NAME    "addrbook"
  47. #define    TMP_ADRBK_NAME    "addrbook.tmp"
  48. #define    WRITE_MODE    "wb"
  49. #define    READ_MODE    "rb"
  50. #else
  51. #define    ADRBK_NAME    ".addressbook"
  52. #define    TMP_ADRBK_NAME    ".addressbook.temp"
  53. #define    WRITE_MODE    "w"
  54. #define    READ_MODE    "r"
  55. #endif
  56.  
  57.  
  58. #ifndef MAXPATH
  59. #define MAXPATH 1000    /* Longest file path we can deal with */
  60. #endif
  61.  
  62. #define MAXLINE 1000    /* Longest line in addrbook */
  63.  
  64. /*
  65.  * The do-while stuff is so these are statements and can be written with
  66.  * a following ; like a regular statement without worrying about braces and all.
  67.  */
  68. #define SKIP_SPACE(p) do{while(*p && *p == SPACE)p++;}while(0)
  69. #define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
  70. #define RM_END_SPACE(start,end)                                             \
  71.         do{char *_ptr = end;                                            \
  72.            while(--_ptr >= start && *_ptr == SPACE)*_ptr = '\0';}while(0)
  73. #define REPLACE_NEWLINES_WITH_SPACE(p)                                      \
  74.         do{register char *_qq;                                      \
  75.            for(_qq = p; *_qq; _qq++)                                \
  76.                if(*_qq == '\n' || *_qq == '\r')                     \
  77.                *_qq = SPACE;}while(0)
  78. #define NO_UID  ((adrbk_uid_t)0)
  79. #define DEFAULT_HTABLE_SIZE 100
  80. #define MAX_CHARS_IN_HASH 50
  81. #define DELETED "#DELETED-"
  82. #define DELETED_LEN 9
  83.  
  84. /*
  85.  * MSC ver 7.0 and less times are since 1900, everybody else's time so far
  86.  * is since 1970. sheesh.
  87.  */
  88. #if    defined(DOS) && (_MSC_VER == 700)
  89. #define EPOCH_ADJ    ((time_t)((time_t)(70*365 + 18) * (time_t)86400))
  90. #endif
  91.  
  92. extern jmp_buf addrbook_changed_unexpectedly;  /* from addrbook.c */
  93. extern char *trouble_filename;                 /*   ditto         */
  94. static forced_rebuilds = 0;  /* forced rebuild even though mtime looks right */
  95. static trouble_rebuilds = 0; /* all rebuilds caused by goto trouble;         */
  96. #define MAX_FORCED_REBUILDS 3
  97. #define MAX_TROUBLE_REBUILDS 10
  98. static int writing; /* so we can give understandable error message */
  99. static adrbk_cntr_t deleted_elem=NO_NEXT;  /* pretend deleted_elem is deleted */
  100. static AdrBk_Entry *deleted_ae=NULL;
  101. static adrbk_cntr_t insert_before=NO_NEXT; /* pretend inserted_entryref comes */
  102. static EntryRef inserted_entryref;         /* before insert_before            */
  103. static adrbk_cntr_t moved_elem=NO_NEXT;    /* pretend moved_elem is moved to  */
  104. static adrbk_cntr_t move_elem_before;      /* right before move_elem_before   */
  105.  
  106. static char empty[]    = "";
  107.  
  108. jmp_buf jump_over_qsort;
  109.  
  110. /*
  111.  * The addrbook entry cache is stored in a hash table.  Each list in the
  112.  * table has up to CACHE_PER_ER_BUCKET entries in it.  The number of hash
  113.  * buckets is adjusted to make this true.  So, for example, if you set the
  114.  * nominal_max_cache_size to 200 and CACHE_PER_ER_BUCKET to 10, there would
  115.  * be a hash array of 20 lists, each with 10 cache entries stored in it.
  116.  * This gives us a way to have constant lookup time (look through no more
  117.  * than 10 entries) regardless of the size of the cache.
  118.  */
  119. #define CACHE_PER_ER_BUCKET 10
  120.  
  121.  
  122. adrbk_cntr_t ab_hash PROTO((char *, a_c_arg_t));
  123. adrbk_cntr_t ab_hash_addr PROTO((char *, a_c_arg_t));
  124. adrbk_uid_t  ab_uid PROTO((char *));
  125. adrbk_uid_t  ab_uid_addr PROTO((char *));
  126. EntryRef    *adrbk_get_entryref PROTO((AdrBk *, a_c_arg_t, Handling));
  127. int          adrbk_write PROTO((AdrBk *, adrbk_cntr_t *, int));
  128. int          bld_hash_from_ondisk_hash PROTO((AdrBk *));
  129. int          build_ondisk_hash_from_abook PROTO((AdrBk *, char *));
  130. void         clear_entryref_cache PROTO((AdrBk *));
  131. void         clearrefs_in_cached_aes PROTO((AdrBk *));
  132. int          cmp_addr PROTO((const QSType *, const QSType *));
  133. int          cmp_cntr_by_full PROTO((const QSType *, const QSType *));
  134. int          cmp_cntr_by_full_lists_last PROTO((const QSType *,const QSType *));
  135. int          cmp_cntr_by_nick PROTO((const QSType *, const QSType *));
  136. int          cmp_cntr_by_nick_lists_last PROTO((const QSType *,const QSType *));
  137. int          cmp_ae_by_full PROTO((const QSType *, const QSType *));
  138. int          cmp_ae_by_full_lists_last PROTO((const QSType *, const QSType *));
  139. int          cmp_ae_by_nick PROTO((const QSType *, const QSType *));
  140. int          cmp_ae_by_nick_lists_last PROTO((const QSType *, const QSType *));
  141. AdrBk_Entry *copy_ae PROTO((AdrBk_Entry *));
  142. void         exp_add_nth PROTO((EXPANDED_S *, a_c_arg_t));
  143. void         exp_del_nth PROTO((EXPANDED_S *, a_c_arg_t));
  144. void         fix_sort_rule_in_hash PROTO((AdrBk *));
  145. void         free_ab_adrhash PROTO((AdrHash **));
  146. void         free_ab_entryref PROTO((AdrBk *, EntryRef *));
  147. char        *get_entryref_line_from_disk PROTO((FILE *, char *, a_c_arg_t));
  148. int          get_sort_rule_from_disk PROTO((FILE *));
  149. time_t       get_adj_fp_file_mtime PROTO((FILE *));
  150. time_t       get_adj_name_file_mtime PROTO((char *));
  151. time_t       get_adj_time PROTO((void));
  152. time_t       get_timestamp_from_disk PROTO((FILE *));
  153. adrbk_cntr_t hashtable_size PROTO((a_c_arg_t));
  154. void         init_adrhash_array PROTO((AdrHash *, a_c_arg_t));
  155. AdrBk_Entry *init_ae_entry PROTO((AdrBk *, EntryRef *));
  156. void         init_entryref_cache PROTO((AdrBk *));
  157. int          length_of_entry PROTO((FILE *, long));
  158. AdrHash     *new_adrhash PROTO((a_c_arg_t));
  159. EntryRef    *new_entryref PROTO((adrbk_uid_t, adrbk_uid_t, long));
  160. int          ok_to_blast_it PROTO((FILE *));
  161. adrbk_cntr_t re_sort_particular_entryref PROTO((AdrBk *, a_c_arg_t));
  162. void         set_inserted_entryref PROTO((AdrBk *, a_c_arg_t, AdrBk_Entry *));
  163. void         set_moved_entryref PROTO((a_c_arg_t, a_c_arg_t));
  164. char        *skip_to_next_addr PROTO((char *));
  165. char        *skip_to_next_nickname PROTO((FILE *, long *, char **, long *,
  166.                                 int, int *));
  167. void         sort_addr_list PROTO((char **));
  168. void         strip_addr_string PROTO((char *, char **, char **));
  169. int          valid_hfile PROTO((FILE *));
  170. int          write_hash_header PROTO((FILE *, a_c_arg_t));
  171. int          write_hash_table PROTO((AdrHash *, FILE *, a_c_arg_t));
  172. int          write_hash_trailer PROTO((AdrBk *, FILE *, int));
  173. int          write_single_abook_entry PROTO((AdrBk_Entry *, FILE *, int *,
  174.                             int *, int *, int *));
  175. int          write_single_entryref PROTO((EntryRef *, FILE *));
  176.  
  177.  
  178. /*
  179.  *  Open, read, and parse an address book.
  180.  *
  181.  * Args: filename -- the filename to open if specified
  182.  *       homedir  -- the user's home directory if specified
  183.  *       warning  -- put "why failed" message to user here
  184.  *
  185.  * If filename is NULL, the default will be used in the homedir
  186.  * passed in.  If homedir is NULL, the current dir will be used.
  187.  * If filename is not NULL and is an absolute path, just the filename
  188.  * will be used.  Otherwise, it will be used relative to the homedir, or
  189.  * to the current dir depending on whether or not homedir is NULL.
  190.  *
  191.  * Expected addressbook file format is:
  192.  *  <nickname>\t<fullname>\t<address_field>\t<fcc>\t<comment>
  193.  *
  194.  * The last two fields (\t<fcc>\t<comment>) are optional.
  195.  *
  196.  * Lines that start with SPACE are continuation lines.  Ends of lines are
  197.  * treated as if they were spaces.  The address field is either a single
  198.  * address or a list of comma-separated addresses inside parentheses.
  199.  *
  200.  * Fields missing from the end of an entry are considered blank.
  201.  *
  202.  * Commas in the address field will cause problems, as will tabs in any
  203.  * field.
  204.  *
  205.  * There may be some deleted entries in the addressbook that don't get
  206.  * used.  They look like regular entries except their nicknames start
  207.  * with the string "#DELETED-YY/MM/DD#".
  208.  */
  209. AdrBk *
  210. adrbk_open(filename, homedir, warning, sort_rule, just_create_lu, lu_not_valid)
  211.     char *filename,
  212.      *homedir,
  213.      *warning;
  214.     int   sort_rule,
  215.       just_create_lu,  /* for special use, create .lu file and that's it */
  216.       lu_not_valid;
  217. {
  218.     register char *p;
  219.     char           path[MAXPATH], *lc;
  220.     AdrBk         *ab;
  221.     int            got_it, create_it;
  222.     int            tried_shortname = 0;
  223.     int            tried_tmpfile = 0;
  224.     int           we_cancel = 0;
  225.  
  226.  
  227.     dprint(2, (debugfile, "- adrbk_open(%s) -\n", filename));
  228.  
  229.     ab        = (AdrBk *)fs_get(sizeof(AdrBk));
  230.     memset(ab, 0, sizeof(AdrBk));
  231.  
  232.     ab->orig_filename = filename ? cpystr(filename) : NULL;
  233.  
  234.     /*------------ figure out and save name of file to open ---------*/
  235.     if(filename == NULL){
  236.         if(homedir != NULL){
  237.         build_path(path, homedir, ADRBK_NAME);
  238.             ab->filename = cpystr(path);
  239.         }
  240.     else
  241.           ab->filename = cpystr(ADRBK_NAME);
  242.     }
  243.     else{
  244.     if(is_absolute_path(filename)){
  245.             ab->filename = cpystr(filename);
  246.         }
  247.     else{
  248.             if(homedir != NULL){
  249.         build_path(path, homedir, filename);
  250.                 ab->filename = cpystr(path);
  251.             }
  252.         else
  253.               ab->filename = cpystr(filename);
  254.         }
  255.     }
  256.  
  257.     if(p = last_cmpnt(ab->filename)){
  258.     strncpy(path, ab->filename, p - ab->filename - 1);
  259.         path[p - ab->filename - 1] = '\0';
  260.     p = path;
  261.     }
  262.     else
  263.       p = ".";
  264.  
  265.     ab->temp_filename = temp_nam(p, "a1");
  266.     ab->temp_hashfile = temp_nam(p, "a2");
  267.     if(!ab->temp_filename || !ab->temp_hashfile){
  268.     if(warning)
  269.       (void)strcpy(warning, "Can't create temporary file");
  270.  
  271.     goto bail_out;
  272.     }
  273.  
  274.     /*
  275.      * If temp_hashfile isn't in the same directory as hashfile, then
  276.      * we must not have permission to write in that directory.  The
  277.      * rename from temp_hashfile to hashfile isn't going to work.  So
  278.      * set it to NULL in that case to let us know there is a problem.
  279.      */
  280.     if(!in_dir(p, ab->temp_hashfile))
  281.       fs_give((void **)&ab->temp_hashfile);  /* sets it to NULL */
  282.  
  283.     if(!in_dir(p, ab->temp_filename))
  284.       fs_give((void **)&ab->temp_filename);
  285.  
  286.     /* open addrbook for reading */
  287.     ab->fp = fopen(ab->filename, READ_MODE);
  288.     if(ab->fp == NULL){
  289.     if(just_create_lu){
  290.         if(warning)
  291.           (void)strcpy(warning, "Address book doesn't exist");
  292.  
  293.         goto bail_out;
  294.     }
  295.  
  296.         /*--- No address book, try creating one ----*/
  297.     q_status_message1(SM_INFO, 0, 3,
  298.         "Address book %s doesn't exist, creating",
  299.         (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
  300.     dprint(2, (debugfile, "Address book %s doesn't exist, creating\n",
  301.         ab->filename));
  302.         ab->fp = fopen(ab->filename, "w");
  303.         if(ab->fp == NULL         ||
  304.         fclose(ab->fp) == EOF ||
  305.         (ab->fp = fopen(ab->filename, READ_MODE)) == NULL){
  306.             /*--- Create failed, bail out ---*/
  307.         if(warning)
  308.           (void)strcpy(warning, error_description(errno));
  309.  
  310.         dprint(2, (debugfile, "create failed: %s\n",
  311.         error_description(errno)));
  312.         goto bail_out;
  313.         }
  314.     }
  315.  
  316.     /* record new change date of addrbook file */
  317.     if(just_create_lu)
  318.       ab->last_change_we_know_about = (time_t)(-1);
  319.     else
  320.       ab->last_change_we_know_about = get_adj_fp_file_mtime(ab->fp);
  321.  
  322.     init_entryref_cache(ab);
  323.  
  324.     ab->hashfile = cpystr(strcat(strcpy(path, ab->filename),
  325.     ADRHASH_FILE_SUFFIX));
  326.  
  327. try_again:
  328.     if(can_access(ab->hashfile, ACCESS_EXISTS) == 0){
  329.     if(can_access(ab->hashfile, EDIT_ACCESS) == 0)
  330.       ab->hashfile_access = ReadWrite;
  331.     else if(can_access(ab->hashfile, READ_ACCESS) == 0)
  332.       ab->hashfile_access = ReadOnly;
  333.     else
  334.       ab->hashfile_access = NoAccess;
  335.     }
  336.     else
  337.       ab->hashfile_access = NoExists;
  338.  
  339.     if(ab->hashfile_access == ReadOnly || ab->hashfile_access == NoAccess)
  340.       ab->sort_rule = AB_SORT_RULE_NONE;
  341.     else
  342.       ab->sort_rule = sort_rule;
  343.  
  344.     got_it = 0;
  345.     create_it = 0;
  346.     if(ab->hashfile_access == ReadWrite || ab->hashfile_access == ReadOnly){
  347.     time_t mtime, timestamp;
  348.  
  349.     /* check to see if up-to-date */
  350.     mtime = get_adj_fp_file_mtime(ab->fp);
  351.     if(mtime != (time_t)(-1)){
  352.         ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  353.         if(lu_not_valid)
  354.           dprint(2, (debugfile, "lu forced not valid\n"));
  355.  
  356.         if(!lu_not_valid && valid_hfile(ab->fp_hash)){
  357.             if((timestamp=get_timestamp_from_disk(ab->fp_hash)) >= mtime){
  358.             /* Ok, hashfile is up-to-date, use it */
  359.             we_cancel = busy_alarm(1, NULL, NULL, 0);
  360.             if(bld_hash_from_ondisk_hash(ab) == 0)
  361.               got_it++;
  362.  
  363.             if(we_cancel)
  364.               cancel_busy_alarm(-1);
  365.             }
  366.             else
  367.               dprint(2, (debugfile,
  368.               "lu is out of date: timestamp=%lu file_mtime=%lu\n",
  369.               (unsigned long)timestamp, (unsigned long)mtime));
  370.         }
  371.         else{
  372.         if(!just_create_lu && !ok_to_blast_it(ab->fp_hash)){
  373.             int ans;
  374.             char prompt[500];
  375.  
  376.             if(ab->hashfile_access == ReadWrite){
  377.                 dprint(2, (debugfile, "ask if ok to blast %s\n",
  378.                 ab->hashfile));
  379.             sprintf(prompt,
  380.                 "Pine needs to update lookup file %s, ok",
  381.                 ab->hashfile);
  382.             ans = want_to(prompt, 'y', 'n', NO_HELP, 0, 0);
  383.             if(ans != 'y'){
  384.                 dprint(2, (debugfile,
  385.                 "user says not ok to blast\n"));
  386.                 if(warning && !*warning)
  387.                   (void)strcpy(warning,
  388.                     "Can't create lookup database");
  389.  
  390.                 if(ab->fp_hash){
  391.                 (void)fclose(ab->fp_hash);
  392.                 ab->fp_hash = (FILE *)NULL;
  393.                 }
  394.  
  395.                 goto try_tempfile;
  396.             }
  397.             else
  398.               dprint(2, (debugfile, "user says ok to blast\n"));
  399.             }
  400.         }
  401.         }
  402.  
  403.         if(!got_it && ab->fp_hash){
  404.         (void)fclose(ab->fp_hash);
  405.         ab->fp_hash = (FILE *)NULL;
  406.         }
  407.     }
  408.     }
  409.  
  410.     if(!got_it){
  411.     if(ab->hashfile_access == ReadWrite)
  412.       create_it++;
  413.     /* See if can create it */
  414.     else if(ab->hashfile_access == NoExists){
  415.         FILE *fp;
  416.         
  417. #ifdef    DOS
  418.        {char *dot;
  419.           if((dot = strindex(ab->hashfile, '.'))
  420.          && *(++dot)
  421.          && strindex(dot, '.')){
  422.           fp = NULL;
  423.           q_status_message1(SM_ORDER, 4, 4,
  424.                "Can't create %s, don't use \".\" in addrbook name",
  425.                (lc=last_cmpnt(ab->hashfile)) ? lc : ab->hashfile);
  426.           display_message('x');
  427.           dprint(2, (debugfile, "Can't create \"%s\", change name of\n  addrbook \"%s\" so that it doesn't have a \".\" in it.\n  Using temp file for now.\n", ab->hashfile, ab->filename));
  428.           errno = 0;
  429.        }
  430.        else{
  431. #endif
  432.         q_status_message1(SM_INFO, 0, 3,
  433.         "Lookup file %s doesn't exist, creating",
  434.         (lc=last_cmpnt(ab->hashfile)) ? lc : ab->hashfile);
  435.         display_message('x');
  436.         dprint(2, (debugfile, "Lookup file %s doesn't exist, creating\n",
  437.         ab->hashfile));
  438.         errno = 0;
  439.         fp = fopen(ab->hashfile, WRITE_MODE);
  440. #ifdef    DOS
  441.       }
  442.     }
  443. #endif
  444.         if(fp == NULL){
  445.         if(errno == ENAMETOOLONG && !tried_shortname){
  446.  
  447.             /*
  448.              * We know that the addressbook name, say "filename", is
  449.              * short enough, and that "filename.lu" is too long.
  450.              * Try a name the same length as "filename".
  451.              * "filename.lu" -> "filen_.l".
  452.              * Note that this may not be the best of ideas.  If more
  453.              * than one address book maps to the same name, we're
  454.              * probably in for some trouble.
  455.              */
  456.              tried_shortname++;
  457.              p    = &(ab->hashfile[strlen(ab->hashfile) - 6]);
  458.              *p++ = '_';
  459.              *p++ = '.';
  460.              *p++ = 'l';
  461.              *p   = '\0';
  462.              q_status_message1(SM_INFO, 0, 3,
  463.              "filename too long, using %s",
  464.              (lc=last_cmpnt(ab->hashfile)) ? lc : ab->hashfile);
  465.              dprint(2, (debugfile, "name too long, trying %s\n",
  466.              ab->hashfile));
  467.              goto try_again;
  468.         }
  469.         else{
  470.             /* have to try /tmp file below */
  471.             if(tried_tmpfile || just_create_lu){
  472.             q_status_message(SM_ORDER, 0, 3,
  473.                 "problems accessing addressbook");
  474.             display_message('x');
  475.             goto bail_out;
  476.             }
  477.         }
  478.         }
  479.         else{
  480.         (void)fclose(fp);
  481.         create_it++;
  482.         }
  483.     }
  484.  
  485.  
  486.     /*
  487.      * If we can't create the hashfile in the right place, put it in
  488.      * a temporary file for the duration of this session.
  489.      */
  490.     if(!create_it && !tried_tmpfile){
  491.         char savename[500];
  492.         int  dir_problem = 0;
  493.  
  494. try_tempfile:
  495.         if(just_create_lu)
  496.           goto bail_out;
  497.  
  498.         tried_tmpfile++;
  499.         (void)strcpy(savename, ab->hashfile);
  500.         if(ab->hashfile)
  501.           fs_give((void **)&ab->hashfile);
  502.  
  503.         if(ab->temp_hashfile)
  504.           fs_give((void **)&ab->temp_hashfile);
  505.         else
  506.           dir_problem++;
  507.  
  508.         ab->hashfile = temp_nam(NULL, "a3");
  509.         if(ab->hashfile == NULL)
  510.           goto bail_out;
  511.  
  512.         dprint(2, (debugfile, "trying tmpfile %s\n", ab->hashfile));
  513.         ab->temp_hashfile = temp_nam(NULL, "a4");
  514.         if(ab->temp_hashfile == NULL)
  515.           goto bail_out;
  516.  
  517.         ab->delete_hashfile = 1;
  518.         if(dir_problem)
  519.           q_status_message1(SM_ORDER, 3, 3,
  520.             "Can't write in directory containing %s, using temp file",
  521.             (lc=last_cmpnt(savename)) ? lc : savename);
  522.         else
  523.           q_status_message1(SM_ORDER, 3, 3,
  524.             "Can't create %s, using temp file",
  525.             (lc=last_cmpnt(savename)) ? lc : savename);
  526.  
  527.         goto try_again;
  528.     }
  529.  
  530.     if(create_it){
  531.         dprint(2, (debugfile, "%s is not valid, rebuilding\n",
  532.          ab->hashfile));
  533.         if(ab->hashfile_access != NoExists)
  534.           if(lu_not_valid)
  535.             q_status_message1(SM_INFO, 0, 1, "forcing rebuild of %s...",
  536.              (lc=last_cmpnt(ab->hashfile)) ? lc : ab->hashfile);
  537.           else
  538.             q_status_message1(SM_INFO, 0,1, "%s isn't valid, rebuilding...",
  539.              (lc=last_cmpnt(ab->hashfile)) ? lc : ab->hashfile);
  540.  
  541.         if(!just_create_lu)
  542.           display_message('x');
  543.  
  544.         if(!just_create_lu)
  545.           we_cancel = busy_alarm(2, "still rebuilding", NULL, 0);
  546.  
  547.         /*
  548.          * If temp_hashfile is NULL, that is because we can't write
  549.          * in the directory where hashfile is, so we need to try to
  550.          * put it in a temp directory instead.
  551.          */
  552.         if(!ab->temp_hashfile && !tried_tmpfile)
  553.           goto try_tempfile;
  554.  
  555.         if(build_ondisk_hash_from_abook(ab,
  556.                     (warning && !*warning) ? warning : NULL)){
  557.         if(we_cancel)
  558.               cancel_busy_alarm(-1);
  559.  
  560.         dprint(2,
  561.          (debugfile, "failed in build_ondisk_hash_from_abook\n"));
  562.         goto bail_out;  /* Failed */
  563.         }
  564.  
  565.         if(we_cancel)
  566.           cancel_busy_alarm(-1);
  567.     }
  568.     }
  569.  
  570.     if(ab){
  571.     /* allocate header for expanded lists list */
  572.     ab->exp      = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
  573.     /* first real element is NULL */
  574.     ab->exp->next = (EXPANDED_S *)NULL;
  575.  
  576.     /* allocate header for checked entries list */
  577.     ab->checks       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
  578.     /* first real element is NULL */
  579.     ab->checks->next = (EXPANDED_S *)NULL;
  580.  
  581.     return(ab);
  582.     }
  583.  
  584. bail_out:
  585.     if(ab->fp)
  586.       (void)fclose(ab->fp);
  587.  
  588.     if(ab->fp_hash)
  589.       (void)fclose(ab->fp_hash);
  590.  
  591.     if(ab->hashfile){
  592.     unlink(ab->hashfile);
  593.     fs_give((void **)&ab->hashfile);
  594.     }
  595.  
  596.     if(ab->orig_filename)
  597.       fs_give((void **)&ab->orig_filename);
  598.  
  599.     if(ab->filename)
  600.       fs_give((void **)&ab->filename);
  601.  
  602.     if(ab->temp_filename)
  603.       fs_give((void **)&ab->temp_filename);
  604.  
  605.     if(ab->temp_hashfile)
  606.       fs_give((void **)&ab->temp_hashfile);
  607.  
  608.     fs_give((void **)&ab);
  609.  
  610.     return NULL;
  611. }
  612.  
  613.  
  614. /*
  615.  * Checks whether or not the addrbook is sorted correctly according to
  616.  * the SortType.  Returns 1 if is sorted correctly, 0 otherwise.
  617.  */
  618. int
  619. adrbk_is_in_sort_order(ab, be_quiet)
  620.     AdrBk *ab;
  621.     int    be_quiet;
  622. {
  623.     adrbk_cntr_t entry;
  624.     AdrBk_Entry *ae, *ae_prev;
  625.     int (*cmp_func)();
  626.     int last_time_sorted_rule;
  627.     int we_cancel = 0;
  628.  
  629.     dprint(9, (debugfile, "- adrbk_is_in_sort_order -\n"));
  630.  
  631.     if(!ab)
  632.       return 0;
  633.  
  634.     if(ab->sort_rule == AB_SORT_RULE_NONE)
  635.       return 1;
  636.     
  637.     if(ab->count < 2)
  638.       return 1;
  639.  
  640.     /*
  641.      * If it's the same, we can assume it is in sort order.
  642.      * This is only actually true if the client is playing by the
  643.      * rules.  The rule that matters here is that you have to sort
  644.      * the addrbook before you can do stuff like add to it or delete
  645.      * from it.  Those operations assume it is already sorted and
  646.      * will record that fact in the variable we're checking below.
  647.      * We're ok because addrbook.c always sorts the addrbook when it
  648.      * finds it is out of order.
  649.      */
  650.     last_time_sorted_rule = get_sort_rule_from_disk(ab->fp_hash);
  651.     if(last_time_sorted_rule != -1 && ab->sort_rule == last_time_sorted_rule)
  652.       return 1;
  653.  
  654.     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  655.                         cmp_ae_by_full_lists_last :
  656.                (ab->sort_rule == AB_SORT_RULE_FULL) ?
  657.                         cmp_ae_by_full :
  658.                (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  659.                         cmp_ae_by_nick_lists_last :
  660.             /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  661.                         cmp_ae_by_nick;
  662.  
  663.     ae_prev = adrbk_get_ae(ab, (a_c_arg_t)0, Normal);
  664.  
  665.     if(!be_quiet)
  666.       we_cancel = busy_alarm(1, NULL, NULL, 0);
  667.  
  668.     for(entry = 1, ae = adrbk_get_ae(ab, (a_c_arg_t)entry, Normal);
  669.     ae != (AdrBk_Entry *)NULL;
  670.     ae = adrbk_get_ae(ab, (a_c_arg_t)(++entry), Normal)){
  671.  
  672.         if((*cmp_func)((QSType *)&ae_prev, (QSType *)&ae) > 0){
  673.         if(we_cancel)
  674.           cancel_busy_alarm(-1);
  675.  
  676.         return 0;
  677.         }
  678.  
  679.         ae_prev = ae;
  680.     }
  681.  
  682.     /*
  683.      * Do this so that we won't have to go through the whole addrbook
  684.      * to check next time we open it.
  685.      */
  686.     fix_sort_rule_in_hash(ab);
  687.  
  688.     if(we_cancel)
  689.       cancel_busy_alarm(-1);
  690.  
  691.     return 1;
  692. }
  693.  
  694.  
  695. void
  696. fix_sort_rule_in_hash(ab)
  697.     AdrBk *ab;
  698. {
  699.     register FILE *fp_for_old_hash;
  700.     register FILE *fp_for_new_hash;
  701.     register int c;
  702.     long filesize, all_but_sort_rule;
  703.  
  704.     if(!ab || ab->fp_hash == (FILE *)NULL)
  705.       return;
  706.  
  707.     filesize = (SIZEOF_HDR + ab->count * SIZEOF_ENTRYREF_ENTRY +
  708.     2 * ab->htable_size * SIZEOF_HTABLE_ENTRY + SIZEOF_TRLR);
  709.     
  710.     all_but_sort_rule = filesize - SIZEOF_SORT_RULE - SIZEOF_NEWLINE;
  711.  
  712.     if((fp_for_new_hash = fopen(ab->temp_hashfile, WRITE_MODE)) == NULL)
  713.       return;
  714.     
  715.     fp_for_old_hash = ab->fp_hash;
  716.     rewind(fp_for_old_hash);
  717.  
  718.     /*
  719.      * Straight copy of all but the sort rule at the end.
  720.      * Everything else is ok.  The in core stuff is ok, too.  This is
  721.      * because the sort rule in the file is used only for the purpose
  722.      * of checking to see if it's already sorted.
  723.      */
  724.     while(all_but_sort_rule-- > 0L){
  725.     if((c = getc(fp_for_old_hash)) == EOF ||
  726.        putc(c, fp_for_new_hash) == EOF){
  727.  
  728.         /* shouldn't happen */
  729.         (void)fclose(fp_for_new_hash);
  730.         (void)unlink(ab->temp_hashfile);
  731.         return;
  732.     }
  733.     }
  734.  
  735.     /* add the sort rule (the 2 is SIZEOF_SORT_RULE) */
  736.     if(fprintf(fp_for_new_hash, "%2d\n", ab->sort_rule) == EOF){
  737.     (void)fclose(fp_for_new_hash);
  738.     (void)unlink(ab->temp_hashfile);
  739.     return;
  740.     }
  741.  
  742.     if(fclose(fp_for_new_hash) == EOF){
  743.     (void)unlink(ab->temp_hashfile);
  744.     return;
  745.     }
  746.  
  747.     file_attrib_copy(ab->temp_hashfile, ab->hashfile);
  748.     (void)fclose(ab->fp_hash);
  749.     if(rename_file(ab->temp_hashfile, ab->hashfile) < 0)
  750.       (void)unlink(ab->temp_hashfile);
  751.  
  752.     ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  753. }
  754.  
  755.  
  756. /*
  757.  * Returns 1 if it is ok to overwrite the file.
  758.  */
  759. int
  760. ok_to_blast_it(fp)
  761.     FILE *fp;
  762. {
  763.     char buf[SIZEOF_PMAGIC + 1];
  764.     long filesize;
  765.  
  766.     if(fp == (FILE *)NULL)
  767.       return 0;
  768.  
  769.     /* check if file is empty */
  770.     if((filesize = fp_file_size(fp)) == -1L)
  771.       return 0;
  772.  
  773.     if(filesize == 0L)
  774.       return 1;
  775.  
  776.     /* check for header PMAGIC (or LEGACY_PMAGIC) */
  777.     if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
  778.       return 0;
  779.  
  780.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
  781.       return 0;
  782.  
  783.     buf[SIZEOF_PMAGIC] = '\0';
  784.     if(strcmp(buf, PMAGIC) == 0 || strcmp(buf, LEGACY_PMAGIC) == 0)
  785.       return 1;
  786.  
  787.     return 0;
  788. }
  789.  
  790.  
  791. /*
  792.  * Sanity checks on hashfile.
  793.  * Returns 1 if ok.
  794.  */
  795. int
  796. valid_hfile(fp)
  797.     FILE *fp;
  798. {
  799.     char buf[SIZEOF_ASCII_LONG + 1];
  800.     long hashsize, num_elements, filesize;
  801.  
  802.     dprint(9, (debugfile, "- valid_hfile -\n"));
  803.  
  804.     if(fp == (FILE *)NULL)
  805.       return 0;
  806.  
  807.     /* check for header PMAGIC */
  808.     if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0)){
  809.     dprint(2, (debugfile, "lu not valid - can't seek to PMAGIC\n"));
  810.     return 0;
  811.     }
  812.  
  813.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC){
  814.     dprint(2, (debugfile, "lu not valid - can't read PMAGIC\n"));
  815.     return 0;
  816.     }
  817.  
  818.     buf[SIZEOF_PMAGIC] = '\0';
  819.     if(strcmp(buf, PMAGIC) != 0){
  820.     dprint(2, (debugfile, "lu not valid - PMAGIC is %s\n", buf));
  821.     return 0;
  822.     }
  823.  
  824.     /* check for matching version number */
  825.     if(fseek(fp, (long)TO_FIND_VERSION_NUM, 0)){
  826.     dprint(2, (debugfile, "lu not valid - can't seek to VERS_NUM\n"));
  827.     return 0;
  828.     }
  829.  
  830.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
  831.     SIZEOF_VERSION_NUM){
  832.     dprint(2, (debugfile, "lu not valid - can't read VERS_NUM\n"));
  833.     return 0;
  834.     }
  835.  
  836.     buf[SIZEOF_VERSION_NUM] = '\0';
  837.     if(strcmp(buf, ADRHASH_FILE_VERSION_NUM) != 0){
  838.     dprint(2, (debugfile, "lu not valid - VERS_NUM is %s not %s\n",
  839.         buf, ADRHASH_FILE_VERSION_NUM));
  840.     return 0;
  841.     }
  842.  
  843.     /* check for reasonable hashtable size */
  844.     if(fseek(fp, (long)TO_FIND_HTABLE_SIZE, 0)){
  845.     dprint(2, (debugfile, "lu not valid - can't seek to HTABLE_SIZE\n"));
  846.     return 0;
  847.     }
  848.  
  849.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_HASH_SIZE, fp) !=
  850.                             SIZEOF_HASH_SIZE){
  851.     dprint(2, (debugfile, "lu not valid - can't read HTABLE_SIZE\n"));
  852.     return 0;
  853.     }
  854.  
  855.     buf[SIZEOF_HASH_SIZE] = '\0';
  856.     hashsize = atol(buf);
  857.     if(hashsize <= 10L || hashsize > MAX_HASHTABLE_SIZE){
  858.     dprint(2, (debugfile, "lu not valid - hashsize is %s\n", buf));
  859.     return 0;
  860.     }
  861.  
  862.     /* check for trailer PMAGIC */
  863.     if(fseek(fp, (long)TO_FIND_TRLR_PMAGIC, 2)){
  864.     dprint(2, (debugfile, "lu not valid - can't seek to TRL_PMAGIC\n"));
  865.     return 0;
  866.     }
  867.  
  868.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC){
  869.     dprint(2, (debugfile, "lu not valid - can't read TRL_PMAGIC\n"));
  870.     return 0;
  871.     }
  872.  
  873.     buf[SIZEOF_PMAGIC] = '\0';
  874.     if(strcmp(buf, PMAGIC) != 0){
  875.     dprint(2, (debugfile, "lu not valid - TRL_PMAGIC is %s\n", buf));
  876.     return 0;
  877.     }
  878.  
  879.     /* check for reasonable number of entries */
  880.     if(fseek(fp, (long)TO_FIND_COUNT, 2)){
  881.     dprint(2, (debugfile, "lu not valid - can't seek to COUNT\n"));
  882.     return 0;
  883.     }
  884.  
  885.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_COUNT, fp) != SIZEOF_COUNT){
  886.     dprint(2, (debugfile, "lu not valid - can't read COUNT\n"));
  887.     return 0;
  888.     }
  889.  
  890.     buf[SIZEOF_COUNT] = '\0';
  891.     num_elements = atol(buf);
  892.     if(num_elements < 0L || num_elements > MAX_ADRBK_SIZE){
  893.     dprint(2, (debugfile, "lu not valid - COUNT is %s\n", buf));
  894.     return 0;
  895.     }
  896.  
  897.     /* check size of file */
  898.     if((filesize = fp_file_size(fp)) == -1L){
  899.     dprint(2, (debugfile, "lu not valid - fp_file_size failed\n"));
  900.     return 0;
  901.     }
  902.  
  903.     if(filesize != (SIZEOF_HDR + num_elements * SIZEOF_ENTRYREF_ENTRY +
  904.     2 * hashsize * SIZEOF_HTABLE_ENTRY + SIZEOF_TRLR)){
  905.     dprint(2, (debugfile, "lu not valid - filesize is %ld\n", filesize));
  906.     return 0;
  907.     }
  908.  
  909.     return 1;
  910. }
  911.  
  912.  
  913. int
  914. bld_hash_from_ondisk_hash(ab)
  915.     AdrBk *ab;
  916. {
  917.     long               cnt;
  918.     char               buf[SIZEOF_HASH_SIZE + 1];
  919.     char              *p;
  920.     register char     *q;
  921.     long               nick_hash_offset;
  922.     size_t             adrhashtable_size;
  923.     adrbk_cntr_t       i;
  924.     size_t             psize;
  925.     adrbk_cntr_t      *array;
  926.     WIDTH_INFO_S      *widths;
  927.  
  928.     dprint(9, (debugfile, "- bld_hash_from_ondisk_hash -\n"));
  929.  
  930.     if(!ab || !ab->fp_hash)
  931.       return -1;
  932.  
  933.     /* get htable size */
  934.     if(fseek(ab->fp_hash, (long)TO_FIND_HTABLE_SIZE, 0) == 0 &&
  935.        fread(buf, sizeof(char), (unsigned)SIZEOF_HASH_SIZE, ab->fp_hash) ==
  936.                             SIZEOF_HASH_SIZE){
  937.     buf[SIZEOF_HASH_SIZE] = '\0';
  938.     ab->htable_size = atoi(buf);
  939.     }
  940.     else
  941.       return -1;
  942.  
  943.     adrhashtable_size = ab->htable_size * SIZEOF_HTABLE_ENTRY;
  944.  
  945.     psize = max(2 * adrhashtable_size, SIZEOF_TRLR);
  946.     p     = (char *)fs_get(psize);
  947.  
  948.     if(fseek(ab->fp_hash, (long)TO_FIND_TRLR_PMAGIC, 2))
  949.       return -1;
  950.  
  951.     if(fread(p, sizeof(char), (unsigned)SIZEOF_TRLR, ab->fp_hash) !=
  952.                                 SIZEOF_TRLR)
  953.       return -1;
  954.  
  955.     q = p + SIZEOF_PMAGIC + SIZEOF_SPACE;
  956.     cnt = atol(q);
  957.     if(cnt < 0L)
  958.       return -1;
  959.     else
  960.       ab->count = (adrbk_cntr_t)cnt;
  961.  
  962.     q += (SIZEOF_COUNT + SIZEOF_NEWLINE);
  963.     ab->deleted_cnt = atol(q);
  964.  
  965.     q += (SIZEOF_DELETED_CNT + SIZEOF_NEWLINE + SIZEOF_SPACE);
  966.     widths = &ab->widths;
  967.     widths->max_nickname_width = atoi(q);
  968.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  969.     widths->max_fullname_width = atoi(q);
  970.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  971.     widths->max_addrfield_width = atoi(q);
  972.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  973.     widths->max_fccfield_width = atoi(q);
  974.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  975.     widths->third_biggest_fullname_width = atoi(q);
  976.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  977.     widths->third_biggest_addrfield_width = atoi(q);
  978.     q += (SIZEOF_WIDTH + SIZEOF_SPACE);
  979.     widths->third_biggest_fccfield_width = atoi(q);
  980.  
  981.     ab->hash_by_nick = new_adrhash((a_c_arg_t)ab->htable_size);
  982.     ab->hash_by_addr = new_adrhash((a_c_arg_t)ab->htable_size);
  983.  
  984.     nick_hash_offset = SIZEOF_HDR + ab->count * SIZEOF_ENTRYREF_ENTRY;
  985.  
  986.     if(fseek(ab->fp_hash, nick_hash_offset, 0) == 0 &&
  987.        fread(p, sizeof(char), (2 * adrhashtable_size), ab->fp_hash) ==
  988.                            2 * adrhashtable_size){
  989.  
  990.     dprint(9, (debugfile, "initializing hash_by_nick\n"));
  991.     /* initialize hash_by_nick array */
  992.     array = ab->hash_by_nick->harray;
  993.     q = p;
  994.     for(i = 0; i < ab->htable_size; i++){
  995.         array[i] = (adrbk_cntr_t)strtoul(q, (char **)NULL, 10);
  996.         q += SIZEOF_HTABLE_ENTRY;
  997.     }
  998.  
  999.     dprint(9, (debugfile, "initializing hash_by_addr\n"));
  1000.     /* initialize hash_by_addr array */
  1001.     array = ab->hash_by_addr->harray;
  1002.     for(i = 0; i < ab->htable_size; i++){
  1003.         array[i] = (adrbk_cntr_t)strtoul(q, (char **)NULL, 10);
  1004.         q += SIZEOF_HTABLE_ENTRY;
  1005.     }
  1006.     }
  1007.     else{
  1008.     free_ab_adrhash(&ab->hash_by_nick);
  1009.     free_ab_adrhash(&ab->hash_by_addr);
  1010.     fs_give((void **)&p);
  1011.     return -1;
  1012.     }
  1013.  
  1014.     fs_give((void **)&p);
  1015.  
  1016.     return 0;
  1017. }
  1018.  
  1019.  
  1020. char *
  1021. get_entryref_line_from_disk(fp, buf, entry_num)
  1022.     FILE     *fp;
  1023.     char      buf[];
  1024.     a_c_arg_t entry_num;
  1025. {
  1026.     long seek_position;
  1027.     size_t rv;
  1028.  
  1029.     if(!fp){
  1030.     dprint(2, (debugfile, "get_entryref_line_from_disk returning NULL!\n"));
  1031.     dprint(2, (debugfile, "    fp was NULL\n"));
  1032.     return NULL;
  1033.     }
  1034.  
  1035.     seek_position = SIZEOF_HDR + (long)entry_num * SIZEOF_ENTRYREF_ENTRY;
  1036.  
  1037.     if(fseek(fp, seek_position, 0)){
  1038.     dprint(2, (debugfile, "get_entryref_line_from_disk returning NULL!\n"));
  1039.     dprint(2, (debugfile,
  1040.             "    fseek failed, seek_position=%ld, entry_num=%lu, %s\n",
  1041.             seek_position, (unsigned long)entry_num,
  1042.             error_description(errno)));
  1043.     return NULL;
  1044.     }
  1045.  
  1046.     errno = 0;
  1047.     clearerr(fp);
  1048.     if((rv=fread(buf, sizeof(char), (unsigned)SIZEOF_ENTRYREF_ENTRY, fp)) !=
  1049.     SIZEOF_ENTRYREF_ENTRY){
  1050.     int saverrno = errno;
  1051.  
  1052.     dprint(2, (debugfile,
  1053.         "get_entryref_line_from_disk returning NULL!\n"));
  1054.     dprint(2, (debugfile,
  1055.         "    fread returned %ld instead of %d, %s (errno=%d)\n",
  1056.         (long)rv, SIZEOF_ENTRYREF_ENTRY, error_description(saverrno),
  1057.         saverrno));
  1058.     dprint(2, (debugfile, "    seek_position=%ld, entry_num=%lu\n",
  1059.         seek_position, (unsigned long)entry_num));
  1060.     if(rv == 0)
  1061.       dprint(2, (debugfile, "    ferror(fp)=%d, feof(fp)=%d\n",
  1062.         ferror(fp), feof(fp)));
  1063.  
  1064.     return NULL;
  1065.     }
  1066.  
  1067.     buf[SIZEOF_ENTRYREF_ENTRY] = '\0';
  1068.  
  1069.     return(buf);
  1070. }
  1071.  
  1072.  
  1073. time_t
  1074. get_timestamp_from_disk(fp)
  1075.     FILE *fp;
  1076. {
  1077.     char buf[SIZEOF_TIMESTAMP + 1];
  1078.  
  1079.     dprint(9, (debugfile, "- get_timestamp_from_disk -\n"));
  1080.  
  1081.     if(fp == (FILE *)NULL)
  1082.       return (time_t)0;
  1083.  
  1084.     if(fseek(fp, (long)TO_FIND_TIMESTAMP, 2))
  1085.       return (time_t)0;
  1086.  
  1087.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_TIMESTAMP, fp) !=
  1088.                                 SIZEOF_TIMESTAMP)
  1089.       return (time_t)0;
  1090.  
  1091.     buf[SIZEOF_TIMESTAMP] = '\0';
  1092.     return((time_t)strtoul(buf, (char **)NULL, 10));
  1093. }
  1094.  
  1095.  
  1096. /*
  1097.  * Adjust the mtime to return time since Unix epoch.  DOS is off by 70 years.
  1098.  */
  1099. time_t
  1100. get_adj_fp_file_mtime(fp)
  1101.     FILE *fp;
  1102. {
  1103.     time_t mtime;
  1104.  
  1105.     mtime = fp_file_mtime(fp);
  1106.  
  1107. #ifdef EPOCH_ADJ
  1108.     if(mtime != (time_t)(-1))
  1109.       mtime -= EPOCH_ADJ;
  1110. #endif
  1111.  
  1112.     return(mtime);
  1113. }
  1114.  
  1115. time_t
  1116. get_adj_name_file_mtime(name)
  1117.     char *name;
  1118. {
  1119.     time_t mtime;
  1120.  
  1121.     mtime = name_file_mtime(name);
  1122.  
  1123. #ifdef EPOCH_ADJ
  1124.     if(mtime != (time_t)(-1))
  1125.       mtime -= EPOCH_ADJ;
  1126. #endif
  1127.  
  1128.     return(mtime);
  1129. }
  1130.  
  1131. time_t
  1132. get_adj_time()
  1133. {
  1134.     time_t tt;
  1135.  
  1136.     tt = time((time_t *)0);
  1137.  
  1138. #ifdef EPOCH_ADJ
  1139.     tt -= EPOCH_ADJ;
  1140. #endif
  1141.  
  1142.     return(tt);
  1143. }
  1144.  
  1145.  
  1146. int
  1147. get_sort_rule_from_disk(fp)
  1148.     FILE *fp;
  1149. {
  1150.     char buf[SIZEOF_SORT_RULE + 1];
  1151.  
  1152.     dprint(9, (debugfile, "- get_sort_rule_from_disk -\n"));
  1153.  
  1154.     if(!fp)
  1155.       return -1;
  1156.  
  1157.     if(fseek(fp, (long)TO_FIND_SORT_RULE, 2))
  1158.       return -1;
  1159.  
  1160.     if(fread(buf, sizeof(char), (unsigned)SIZEOF_SORT_RULE, fp) !=
  1161.                                 SIZEOF_SORT_RULE)
  1162.       return -1;
  1163.  
  1164.     buf[SIZEOF_SORT_RULE] = '\0';
  1165.     return(atoi(buf));
  1166. }
  1167.  
  1168.  
  1169. /*
  1170.  * Builds the ondisk (and incore) hash file from the ondisk address book.
  1171.  * This only happens if the hash file is missing or corrupt.
  1172.  */
  1173. int
  1174. build_ondisk_hash_from_abook(ab, warning)
  1175.     AdrBk *ab;
  1176.     char *warning;
  1177. {
  1178.     FILE          *fp_for_hash, *fp_in;
  1179.     EntryRef       e;
  1180.     char          *nickname;
  1181.     char          *address;
  1182.     char          *lc;
  1183.     adrbk_cntr_t   used;
  1184.     adrbk_cntr_t   hash;
  1185.     long           offset;
  1186.     int            max_nick = 0,
  1187.            max_addr = 0, addr_two = 0, addr_three = 0,
  1188.            this_nick_width, this_addr_width;
  1189.     int           longline = 0, rew = 1;
  1190.     WIDTH_INFO_S  *widths;
  1191.     unsigned long  filesize;
  1192.  
  1193.     dprint(9, (debugfile, "- build_ondisk_hash_from_abook -\n"));
  1194.  
  1195.     if(!ab || !ab->hashfile || !ab->temp_hashfile || !ab->fp)
  1196.       return -1;
  1197.  
  1198.     errno = 0;
  1199.  
  1200.     if((fp_for_hash = fopen(ab->temp_hashfile, WRITE_MODE)) == NULL)
  1201.       return -1;
  1202.  
  1203.     fp_in = ab->fp;
  1204.  
  1205.     /* get size of file to estimate good hashtable_size */
  1206.     if((filesize = (unsigned long)fp_file_size(fp_in)) == (unsigned long)-1L)
  1207.       ab->htable_size  = DEFAULT_HTABLE_SIZE;
  1208.     else{
  1209.     a_c_arg_t     approx_number_of_entries;
  1210.  
  1211.     approx_number_of_entries = (a_c_arg_t)(filesize / 50);
  1212.     ab->htable_size  = hashtable_size(approx_number_of_entries);
  1213.     }
  1214.  
  1215.     ab->deleted_cnt  = 0L;  /* number #DELETED- */
  1216.     ab->hash_by_nick = new_adrhash((a_c_arg_t)ab->htable_size);
  1217.     ab->hash_by_addr = new_adrhash((a_c_arg_t)ab->htable_size);
  1218.  
  1219.     if(write_hash_header(fp_for_hash, (a_c_arg_t)ab->htable_size))
  1220.       goto io_err;
  1221.  
  1222.     used = 0;
  1223.  
  1224.     while((nickname =
  1225.       skip_to_next_nickname(fp_in,&offset,&address,NULL,
  1226.                 rew,&longline)) != NULL){
  1227.  
  1228.     rew = 0;
  1229.  
  1230.     if(strncmp(nickname, DELETED, DELETED_LEN) == 0
  1231.        && isdigit((unsigned char)nickname[DELETED_LEN])
  1232.        && isdigit((unsigned char)nickname[DELETED_LEN+1])
  1233.        && nickname[DELETED_LEN+2] == '/'
  1234.        && isdigit((unsigned char)nickname[DELETED_LEN+3])
  1235.        && isdigit((unsigned char)nickname[DELETED_LEN+4])
  1236.        && nickname[DELETED_LEN+5] == '/'
  1237.        && isdigit((unsigned char)nickname[DELETED_LEN+6])
  1238.        && isdigit((unsigned char)nickname[DELETED_LEN+7])
  1239.        && nickname[DELETED_LEN+8] == '#'){
  1240.         ab->deleted_cnt++;
  1241.         continue;
  1242.     }
  1243.  
  1244.     ALARM_BLIP();
  1245.     if((long)used > MAX_ADRBK_SIZE){
  1246.         q_status_message2(SM_ORDER | SM_DING, 4, 5,
  1247.                 "Max addrbook size is %s, %s too large, giving up",
  1248.                 long2string(MAX_ADRBK_SIZE),
  1249.                 (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
  1250.         goto io_err;
  1251.     }
  1252.  
  1253.     e.uid_nick  = ab_uid(nickname);
  1254.     this_nick_width = strlen(nickname);
  1255.     e.offset    = offset;
  1256.     e.ae        = (AdrBk_Entry *)NULL;
  1257.     hash        = ab_hash(nickname, (a_c_arg_t)ab->htable_size);
  1258.     e.next_nick = ab->hash_by_nick->harray[hash];
  1259.     ab->hash_by_nick->harray[hash] = used;
  1260.     if(address && *address != '('){ /* not a list */
  1261.         e.uid_addr  = ab_uid_addr(address);
  1262.         this_addr_width = strlen(address);
  1263.         hash        = ab_hash_addr(address, (a_c_arg_t)ab->htable_size);
  1264.         e.next_addr = ab->hash_by_addr->harray[hash];
  1265.         ab->hash_by_addr->harray[hash] = used;
  1266.     }
  1267.     else{
  1268.         if(address && *address)
  1269.           this_addr_width = strlen(address) - 2;
  1270.         else
  1271.           this_addr_width = 0;
  1272.  
  1273.         e.uid_addr  = NO_UID;
  1274.         e.next_addr = NO_NEXT;
  1275.     }
  1276.  
  1277.     used++;
  1278.     if(write_single_entryref(&e, fp_for_hash))
  1279.       goto io_err;
  1280.  
  1281.     /*
  1282.      * Keep track of widths.  These are only approximate.  If we ever
  1283.      * do an adrbk_write we'll get the exact numbers.  We don't have
  1284.      * any idea of fullname widths so we'll just use the same as the
  1285.      * addrfield widths to get the drawing off the ground.  Same for
  1286.      * the fcc widths.
  1287.      */
  1288.     max_nick = max(max_nick, this_nick_width);
  1289.     if(this_addr_width > max_addr){
  1290.         addr_three = addr_two;
  1291.         addr_two   = max_addr;
  1292.         max_addr   = this_addr_width;
  1293.     }
  1294.     else if(this_addr_width > addr_two){
  1295.         addr_three = addr_two;
  1296.         addr_two   = this_addr_width;
  1297.     }
  1298.     else if(this_addr_width > addr_three){
  1299.         addr_three = this_addr_width;
  1300.     }
  1301.     }
  1302.  
  1303.     if(longline){
  1304.     if(warning)
  1305.       (void)strcpy(warning, "line too long: must be fixed by hand");
  1306.  
  1307.     if(ab->fp_hash){
  1308.         (void)fclose(ab->fp_hash);
  1309.         ab->fp_hash = (FILE *)NULL;
  1310.     }
  1311.  
  1312.     free_ab_adrhash(&ab->hash_by_nick);
  1313.     free_ab_adrhash(&ab->hash_by_addr);
  1314.  
  1315.     return -1;
  1316.     }
  1317.     
  1318.     ab->count = used;
  1319.  
  1320.     widths = &ab->widths;
  1321.     widths->max_nickname_width  = max_nick;
  1322.     widths->max_addrfield_width = max_addr;
  1323.     widths->third_biggest_addrfield_width = addr_three;
  1324.     widths->max_fullname_width  = max_addr;
  1325.     widths->max_fccfield_width = max_addr;
  1326.     widths->third_biggest_fullname_width  = addr_three;
  1327.     widths->third_biggest_fccfield_width = addr_three;
  1328.  
  1329.     if(write_hash_table(ab->hash_by_nick, fp_for_hash,
  1330.     (a_c_arg_t)ab->htable_size))
  1331.       goto io_err;
  1332.  
  1333.     if(write_hash_table(ab->hash_by_addr, fp_for_hash,
  1334.     (a_c_arg_t)ab->htable_size))
  1335.       goto io_err;
  1336.  
  1337.     if(write_hash_trailer(ab, fp_for_hash, 0))
  1338.       goto io_err;
  1339.  
  1340.     if(fclose(fp_for_hash) == EOF)
  1341.       goto io_err;
  1342.  
  1343.     file_attrib_copy(ab->temp_hashfile, ab->hashfile);
  1344.     if(ab->fp_hash){
  1345.     (void)fclose(ab->fp_hash);
  1346.     ab->fp_hash = NULL;        /* in case of problems */
  1347.     }
  1348.  
  1349.     if(rename_file(ab->temp_hashfile, ab->hashfile) < 0)
  1350.       goto io_err;
  1351.  
  1352.     ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  1353.  
  1354.     return 0;
  1355.  
  1356. io_err:
  1357.     if(warning && errno != 0)
  1358.       (void)strcpy(warning, error_description(errno));
  1359.  
  1360.     if(ab->fp_hash){
  1361.     (void)fclose(ab->fp_hash);
  1362.     ab->fp_hash = (FILE *)NULL;
  1363.     }
  1364.  
  1365.     free_ab_adrhash(&ab->hash_by_nick);
  1366.     free_ab_adrhash(&ab->hash_by_addr);
  1367.  
  1368.     return -1;
  1369. }
  1370.  
  1371.  
  1372. static char space[] = " ";
  1373.  
  1374. /*
  1375.  * Returns next nickname, or NULL
  1376.  *
  1377.  * The offset arg is the offset of the nickname in the file, returned to caller.
  1378.  * The address arg is a pointer to the address, returned to caller.
  1379.  * The length arg is returned to caller.  It is length of entire entry.
  1380.  * If rew is set, rewind the file and start over at beginning.
  1381.  * Longline is returned equal to 1 if an input line is too long.
  1382.  */
  1383. char *
  1384. skip_to_next_nickname(fp, offset, address, length, rew, longline)
  1385.     FILE  *fp;
  1386.     long  *offset;
  1387.     char **address;
  1388.     long  *length;
  1389.     int    rew;
  1390.     int   *longline;
  1391. {
  1392.     static char line[MAXLINE+1];
  1393.     static char next_nickname[MAX_NICKNAME+1];
  1394.     static char this_nickname[MAX_NICKNAME+1];
  1395.     static char this_address[MAXLINE+1];
  1396.     static char next_address[MAXLINE+1];
  1397.     char *p;
  1398.     int   c;
  1399.     char *nickname;
  1400.     char *addr;
  1401.     static long next_nickname_offset = 0L;
  1402.     int ok_so_far = 0;
  1403.  
  1404.  
  1405.     if(address)
  1406.       *address = this_address;
  1407.  
  1408.     if(rew){
  1409.     rewind(fp);
  1410.     /* skip leading (bogus) continuation lines */
  1411.     do{
  1412.         *offset  = ftell(fp);
  1413.         line[MAXLINE-1] = '\0';
  1414.         p       = fgets(line, MAXLINE+1, fp);
  1415.         if(p == NULL)
  1416.           return NULL;
  1417.         else if(!(p[MAXLINE-1] == '\0'
  1418.            || p[MAXLINE-1] == '\n'
  1419.            || p[MAXLINE-1] == '\r')){
  1420.         for(c = 0; c < max(ps_global->ttyo->screen_cols - 30, 0); c++)
  1421.           if(p[c] == TAB)
  1422.             p[c] = SPACE;
  1423.         p[c] = '\0';
  1424.         q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1425.                    "Addrbook line too long:  %s...", p);
  1426.         dprint(2, (debugfile,
  1427.             "line too long in build_ondisk_hash_from_abook(1): %s...\n",
  1428.             p));
  1429.         if(longline)
  1430.           *longline = 1;
  1431.  
  1432.         return NULL;
  1433.         }
  1434.     }while(*p == SPACE);
  1435.  
  1436.     nickname = p;
  1437.     SKIP_TO_TAB(p);
  1438.     /* This *should* be true. */
  1439.     if(*p == TAB)
  1440.       ok_so_far++;
  1441.  
  1442.     *p = '\0';
  1443.     /*
  1444.      * We want nickname of "" to be treated as an empty nickname, but
  1445.      * not to end the addrbook.
  1446.      */
  1447.     if(!*nickname)
  1448.       nickname = space;
  1449.  
  1450.     strncpy(next_nickname, nickname, MAX_NICKNAME);
  1451.     next_nickname[MAX_NICKNAME] = '\0';
  1452.     next_nickname_offset = *offset;
  1453.     /* locate address field */
  1454.     if(!ok_so_far) /* no tab after nickname */
  1455.       goto no_address_initially;
  1456.  
  1457.     p++;
  1458.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1459.         c = getc(fp);
  1460.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1461.           c = getc(fp);
  1462.  
  1463.         if(c != SPACE)
  1464.           ok_so_far = 0;
  1465.  
  1466.         (void)ungetc(c, fp);
  1467.         if(ok_so_far){
  1468.         line[MAXLINE-1] = '\0';
  1469.         p = fgets(line, MAXLINE+1, fp);
  1470.         if(!(p == NULL
  1471.            || p[MAXLINE-1] == '\0'
  1472.            || p[MAXLINE-1] == '\n'
  1473.            || p[MAXLINE-1] == '\r')){
  1474.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1475.               if(p[c] == TAB)
  1476.             p[c] = SPACE;
  1477.             p[c] = '\0';
  1478.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1479.                    "Addrbook line too long:  %s...", p);
  1480.             dprint(2, (debugfile,
  1481.             "line too long in build_ondisk_hash_from_abook(2): %s...\n",
  1482.             p));
  1483.             if(longline)
  1484.               *longline = 1;
  1485.  
  1486.             return NULL;
  1487.         }
  1488.         }
  1489.  
  1490.         if(!ok_so_far || p == NULL){
  1491.         ok_so_far = 0;
  1492.         goto no_address_initially;
  1493.         }
  1494.     }
  1495.  
  1496.     /* skip fullname field */
  1497.     SKIP_TO_TAB(p);
  1498.     if(*p != TAB){
  1499.         ok_so_far = 0;
  1500.         goto no_address_initially;
  1501.     }
  1502.  
  1503.     p++;
  1504.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1505.         c = getc(fp);
  1506.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1507.           c = getc(fp);
  1508.  
  1509.         if(c != SPACE)
  1510.           ok_so_far = 0;
  1511.  
  1512.         (void)ungetc(c, fp);
  1513.         if(ok_so_far){
  1514.         line[MAXLINE-1] = '\0';
  1515.         p = fgets(line, MAXLINE+1, fp);
  1516.         if(!(p == NULL
  1517.            || p[MAXLINE-1] == '\0'
  1518.            || p[MAXLINE-1] == '\n'
  1519.            || p[MAXLINE-1] == '\r')){
  1520.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1521.               if(p[c] == TAB)
  1522.             p[c] = SPACE;
  1523.             p[c] = '\0';
  1524.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1525.                    "Addrbook line too long:  %s...", p);
  1526.             dprint(2, (debugfile,
  1527.             "line too long in build_ondisk_hash_from_abook(3): %s...\n",
  1528.             p));
  1529.             if(longline)
  1530.               *longline = 1;
  1531.  
  1532.             return NULL;
  1533.         }
  1534.         }
  1535.  
  1536.         if(!ok_so_far || p == NULL){
  1537.         ok_so_far = 0;
  1538.             goto no_address_initially;
  1539.         }
  1540.     }
  1541.  
  1542.     SKIP_SPACE(p);
  1543.  
  1544. no_address_initially:
  1545.  
  1546.     if(ok_so_far){
  1547.         addr = p;
  1548.         SKIP_TO_TAB(p);
  1549.         *p = '\0';
  1550.         strncpy(next_address, addr, MAXLINE);
  1551.     }
  1552.     else
  1553.       next_address[0] = '\0';  /* won't happen with good input data */
  1554.     }
  1555.  
  1556.     if(next_nickname[0] == '\0')
  1557.       return NULL;
  1558.  
  1559.     strcpy(this_nickname, next_nickname);
  1560.     *offset = next_nickname_offset;
  1561.     strcpy(this_address, next_address);
  1562.  
  1563.     /* skip continuation lines */
  1564.     do{
  1565.     next_nickname_offset = ftell(fp);
  1566.     line[MAXLINE-1] = '\0';
  1567.     p = fgets(line, MAXLINE+1, fp);
  1568.     if(!(p == NULL
  1569.        || p[MAXLINE-1] == '\0'
  1570.        || p[MAXLINE-1] == '\n'
  1571.        || p[MAXLINE-1] == '\r')){
  1572.         for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1573.           if(p[c] == TAB)
  1574.         p[c] = SPACE;
  1575.         p[c] = '\0';
  1576.         q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1577.                "Addrbook line too long:  %s...", p);
  1578.         dprint(2, (debugfile,
  1579.         "line too long in build_ondisk_hash_from_abook(4): %s...\n",p));
  1580.         if(longline)
  1581.           *longline = 1;
  1582.  
  1583.         return NULL;
  1584.     }
  1585.     }while(p && *p == SPACE);
  1586.  
  1587.     if(p){
  1588.     nickname = p;
  1589.     SKIP_TO_TAB(p);
  1590.     if(*p == TAB)  /* this should always happen */
  1591.       ok_so_far++;
  1592.     else
  1593.       ok_so_far = 0;
  1594.  
  1595.     *p = '\0';
  1596.     /*
  1597.      * We want nickname of "" to be treated as an empty nickname, but
  1598.      * not to end the addrbook.
  1599.      */
  1600.     if(!*nickname)
  1601.       nickname = space;
  1602.  
  1603.     strncpy(next_nickname, nickname, MAX_NICKNAME);
  1604.     /* locate address field */
  1605.     if(!ok_so_far) /* no tab after nickname */
  1606.       goto no_address;
  1607.  
  1608.     p++;
  1609.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1610.         c = getc(fp);
  1611.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1612.           c = getc(fp);
  1613.  
  1614.         if(c != SPACE)
  1615.           ok_so_far = 0;
  1616.  
  1617.         (void)ungetc(c, fp);
  1618.         if(ok_so_far){
  1619.         line[MAXLINE-1] = '\0';
  1620.         p = fgets(line, MAXLINE+1, fp);
  1621.         if(!(p == NULL
  1622.            || p[MAXLINE-1] == '\0'
  1623.            || p[MAXLINE-1] == '\n'
  1624.            || p[MAXLINE-1] == '\r')){
  1625.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1626.               if(p[c] == TAB)
  1627.             p[c] = SPACE;
  1628.             p[c] = '\0';
  1629.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1630.                    "Addrbook line too long:  %s...", p);
  1631.             dprint(2, (debugfile,
  1632.             "line too long in build_ondisk_hash_from_abook(5): %s...\n",
  1633.             p));
  1634.             if(longline)
  1635.               *longline = 1;
  1636.  
  1637.             return NULL;
  1638.         }
  1639.         }
  1640.  
  1641.         if(!ok_so_far || p == NULL){
  1642.         ok_so_far = 0;
  1643.         goto no_address;
  1644.         }
  1645.     }
  1646.  
  1647.     /* skip fullname field */
  1648.     SKIP_TO_TAB(p);
  1649.     if(*p != TAB){
  1650.         ok_so_far = 0;
  1651.         goto no_address;
  1652.     }
  1653.  
  1654.     p++;
  1655.     if(*p == '\n' || *p == '\r'){ /* get a continuation line */
  1656.         c = getc(fp);
  1657.         if(c == '\n' && *p == '\r')  /* handle CRLF's */
  1658.           c = getc(fp);
  1659.  
  1660.         if(c != SPACE)
  1661.           ok_so_far = 0;
  1662.  
  1663.         (void)ungetc(c, fp);
  1664.         if(ok_so_far){
  1665.         line[MAXLINE-1] = '\0';
  1666.         p = fgets(line, MAXLINE+1, fp);
  1667.         if(!(p == NULL
  1668.            || p[MAXLINE-1] == '\0'
  1669.            || p[MAXLINE-1] == '\n'
  1670.            || p[MAXLINE-1] == '\r')){
  1671.             for(c=0; c<max(ps_global->ttyo->screen_cols-30, 0); c++)
  1672.               if(p[c] == TAB)
  1673.             p[c] = SPACE;
  1674.             p[c] = '\0';
  1675.             q_status_message1(SM_ORDER | SM_DING, 7, 7,
  1676.                    "Addrbook line too long:  %s...", p);
  1677.             dprint(2, (debugfile,
  1678.             "line too long in build_ondisk_hash_from_abook(6): %s...\n",
  1679.             p));
  1680.             if(longline)
  1681.               *longline = 1;
  1682.  
  1683.             return NULL;
  1684.         }
  1685.         }
  1686.  
  1687.         if(!ok_so_far || p == NULL){
  1688.         ok_so_far = 0;
  1689.             goto no_address;
  1690.         }
  1691.     }
  1692.  
  1693.     SKIP_SPACE(p);
  1694.  
  1695. no_address:
  1696.  
  1697.     if(ok_so_far){
  1698.         addr = p;
  1699.         SKIP_TO_TAB(p);
  1700.         *p = '\0';
  1701.         strncpy(next_address, addr, MAXLINE);
  1702.     }
  1703.     else
  1704.       next_address[0] = '\0';  /* shouldn't happen */
  1705.     }
  1706.     else
  1707.       next_nickname[0] = '\0';
  1708.  
  1709.     if(length)
  1710.       *length = next_nickname_offset - *offset;
  1711.  
  1712.     return(this_nickname);
  1713. }
  1714.  
  1715.  
  1716. EntryRef *
  1717. new_entryref(uid_nickname, uid_address, offset)
  1718.     adrbk_uid_t uid_nickname;
  1719.     adrbk_uid_t uid_address;
  1720.     long        offset;
  1721. {
  1722.     EntryRef *e;
  1723.  
  1724.     e            =  (EntryRef *)fs_get(sizeof(EntryRef));
  1725.     e->uid_nick  =  uid_nickname;
  1726.     e->uid_addr  =  uid_address;
  1727.     e->offset    =  offset;
  1728.     e->next_nick =  NO_NEXT;
  1729.     e->next_addr =  NO_NEXT;
  1730.     e->ae        =  (AdrBk_Entry *)NULL;
  1731.  
  1732.     return(e);
  1733. }
  1734.  
  1735.  
  1736. /*
  1737.  * Returns the hash value of name, which will be in the range 0 ... size-1.
  1738.  * This is a standard hash function that should be as evenly distributed
  1739.  * as possible.  I haven't done much research to try to find a good one.
  1740.  * Most important is the distribution of hashing of all the nicknames in
  1741.  * big addrbooks.  Hashing of all the Single addresses is also important.
  1742.  */
  1743. adrbk_cntr_t
  1744. ab_hash(name, size)
  1745.     char     *name;
  1746.     a_c_arg_t size;
  1747. {
  1748.     register usign32_t h = 0L;
  1749.     register usign32_t c;
  1750.     int at_most_this_many_chars_in_hash = MAX_CHARS_IN_HASH;
  1751.     int four_counter = 0;
  1752.     int two_counter = 1;
  1753.  
  1754.     if(!name)
  1755.       return((adrbk_cntr_t)h);
  1756.  
  1757.     /* Make the hash case independent */
  1758.     while((c = *name++) && at_most_this_many_chars_in_hash-- > 0){
  1759.     if(isspace((unsigned char)c))
  1760.       continue;  /* so we don't have to worry about trimming spaces */
  1761.  
  1762.     if(isupper((unsigned char)c))
  1763.       c = tolower((unsigned char)c);
  1764.  
  1765.     /*
  1766.      * We're relying on usign32_t being a true 32 bit unsigned.  If it
  1767.      * is larger than 32 bits, we'll get different hash values on
  1768.      * that system than on a true 32 bit system.  We could probably
  1769.      * figure out some sort of masking strategy to protect ourselves.
  1770.      */
  1771.     switch(four_counter){
  1772.       case 0:
  1773.         h += (two_counter ? (c << 24) : (c << 25));
  1774.         break;
  1775.       case 1:
  1776.         h += (two_counter ? (c << 16) : (c << 17));
  1777.         break;
  1778.       case 2:
  1779.         h += (two_counter ? (c << 8) : (c << 9));
  1780.         break;
  1781.       case 3:
  1782.         h += (two_counter ? c : (c << 1));
  1783.         break;
  1784.     }
  1785.     four_counter = (four_counter + 1) % 4;
  1786.     if(four_counter == 0)
  1787.       two_counter = (two_counter + 1) % 2;
  1788.     }
  1789.     
  1790.     return(h % (adrbk_cntr_t)size);
  1791. }
  1792.  
  1793.  
  1794. /*
  1795.  * This is the same as ab_hash above, but it assumes that name is an address
  1796.  * string and strips off the non-address part before computing the hash value.
  1797.  * That is, it turns Joe College <joe@college.edu> into joe@college.edu
  1798.  * for hashing purposes.
  1799.  */
  1800. adrbk_cntr_t
  1801. ab_hash_addr(name, size)
  1802.     char     *name;
  1803.     a_c_arg_t size;
  1804. {
  1805.     char *start_addr = NULL;
  1806.     char *end_addr   = NULL;
  1807.     adrbk_cntr_t h = (adrbk_cntr_t)0L;
  1808.     char  save;
  1809.  
  1810.     if(!name)
  1811.       return(h);
  1812.     
  1813.     strip_addr_string(name, &start_addr, &end_addr);
  1814.     if(start_addr){
  1815.     save = *end_addr;
  1816.     *end_addr = '\0';
  1817.     h = ab_hash(start_addr, size);
  1818.     *end_addr = save;
  1819.     }
  1820.  
  1821.     return(h);
  1822. }
  1823.  
  1824.  
  1825. AdrHash *
  1826. new_adrhash(size)
  1827.     a_c_arg_t size;
  1828. {
  1829.     AdrHash *a;
  1830.  
  1831.     a = (AdrHash *)fs_get(sizeof(AdrHash));
  1832.     a->harray = (adrbk_cntr_t *)fs_get((size_t)size * sizeof(adrbk_cntr_t));
  1833.  
  1834.     /*
  1835.      * The ff initialization causes the next_nick and next_addr pointers to
  1836.      * be set to NO_NEXT.
  1837.      */
  1838.     memset(a->harray, 0xff, (size_t)size * sizeof(adrbk_cntr_t));
  1839.  
  1840.     return(a);
  1841. }
  1842.  
  1843.  
  1844. void
  1845. init_adrhash_array(a, size)
  1846.     AdrHash     *a;
  1847.     a_c_arg_t    size;
  1848. {
  1849.     dprint(9, (debugfile, "- init_adrhash_array -\n"));
  1850.  
  1851.     /*
  1852.      * The ff initialization causes the next_nick and next_addr pointers to
  1853.      * be set to NO_NEXT.
  1854.      */
  1855.     memset(a->harray, 0xff, (size_t)size * sizeof(adrbk_cntr_t));
  1856. }
  1857.  
  1858.  
  1859. void
  1860. free_ab_adrhash(a)
  1861.     AdrHash **a;
  1862. {
  1863.     if(!(*a))
  1864.       return;
  1865.  
  1866.     if((*a)->harray)
  1867.       fs_give((void **)&((*a)->harray));
  1868.  
  1869.     fs_give((void **)a);
  1870. }
  1871.  
  1872.  
  1873. /*
  1874.  * Returns a value which is probably unique for name.  That is, if name1 and
  1875.  * name2 are not the same, then uid(name1) probably not equal to uid(name2).
  1876.  * Actually, they only have to be unique within a given hash bucket.  That
  1877.  * is, we don't want both ab_uid(name1) == ab_uid(name2) and
  1878.  *                       ab_hash(name1) == ab_hash(name2).
  1879.  *
  1880.  * Uid should not be NO_UID so we can tell when it hasn't been initialized.
  1881.  */
  1882. adrbk_uid_t
  1883. ab_uid(name)
  1884.     char *name;
  1885. {
  1886.     register adrbk_uid_t u = (adrbk_uid_t)0;
  1887.     int at_most_this_many_chars_in_uid = MAX_CHARS_IN_HASH;
  1888.     int c;
  1889.  
  1890.     if(!name)
  1891.       return(u);
  1892.  
  1893.     /* Make the uid case independent and only depend on first N chars */
  1894.     while((c = *name++) && at_most_this_many_chars_in_uid-- > 0){
  1895.     if(isspace((unsigned char)c))
  1896.       continue;  /* so we don't have to worry about trimming spaces */
  1897.  
  1898.     if(isupper((unsigned char)c))
  1899.       c = tolower((unsigned char)c);
  1900.  
  1901.     /* this comes from emacs, I think */
  1902.     u = ((((u << 4) & 0xffffffff) + (u >> 24)) & 0x0fffffff) + c;
  1903.     }
  1904.     
  1905.     if(u == NO_UID)
  1906.       u++;
  1907.  
  1908.     return(u);
  1909. }
  1910.  
  1911.  
  1912. /*
  1913.  * This is the same as ab_uid above, but it assumes that name is an address
  1914.  * string and strips off the non-address part before computing the uid value.
  1915.  * That is, it turns Joe College <joe@college.edu> into joe@college.edu
  1916.  * for uid purposes.
  1917.  */
  1918. adrbk_uid_t
  1919. ab_uid_addr(name)
  1920.     char     *name;
  1921. {
  1922.     char *start_addr = NULL;
  1923.     char *end_addr   = NULL;
  1924.     adrbk_uid_t u    = NO_UID;
  1925.     char  save;
  1926.  
  1927.     if(!name)
  1928.       return(NO_UID);
  1929.     
  1930.     strip_addr_string(name, &start_addr, &end_addr);
  1931.     if(start_addr){
  1932.     save = *end_addr;
  1933.     *end_addr = '\0';
  1934.     u = ab_uid(start_addr);
  1935.     *end_addr = save;
  1936.     }
  1937.  
  1938.     return(u);
  1939. }
  1940.  
  1941.  
  1942. /*
  1943.  * Returns a pointer to the start of the mailbox@host part of this
  1944.  * address string, and a pointer to the end + 1.  The caller can then
  1945.  * replace the end char with \0 and call the hash function, then put
  1946.  * back the end char.  Start_addr and end_addr are assumed to be non-null.
  1947.  */
  1948. void
  1949. strip_addr_string(addrstr, start_addr, end_addr)
  1950.     char  *addrstr;
  1951.     char **start_addr;
  1952.     char **end_addr;
  1953. {
  1954.     register char *q;
  1955.     int in_quotes  = 0,
  1956.         in_comment = 0;
  1957.     char prev_char = '\0';
  1958.  
  1959.     if(!addrstr || !*addrstr){
  1960.     *start_addr = NULL;
  1961.     *end_addr = NULL;
  1962.     return;
  1963.     }
  1964.  
  1965.     *start_addr = addrstr;
  1966.  
  1967.     for(q = addrstr; *q; q++){
  1968.     switch(*q){
  1969.       case '<':
  1970.         if(!in_quotes && !in_comment){
  1971.         if(*++q){
  1972.             *start_addr = q;
  1973.             /* skip to > */
  1974.             while(*q && *q != '>')
  1975.               q++;
  1976.  
  1977.             /* found > */
  1978.             if(*q){
  1979.             *end_addr = q;
  1980.             return;
  1981.             }
  1982.             else
  1983.               q--;
  1984.         }
  1985.         }
  1986.  
  1987.         break;
  1988.  
  1989.       case LPAREN:
  1990.         if(!in_quotes && !in_comment)
  1991.           in_comment = 1;
  1992.         break;
  1993.  
  1994.       case RPAREN:
  1995.         if(in_comment && prev_char != BSLASH)
  1996.           in_comment = 0;
  1997.         break;
  1998.  
  1999.       case QUOTE:
  2000.         if(in_quotes && prev_char != BSLASH)
  2001.           in_quotes = 0;
  2002.         else if(!in_quotes && !in_comment)
  2003.           in_quotes = 1;
  2004.         break;
  2005.  
  2006.       default:
  2007.         break;
  2008.     }
  2009.  
  2010.     prev_char = *q;
  2011.     }
  2012.  
  2013.     *end_addr = q;
  2014. }
  2015.  
  2016.  
  2017. /*
  2018.  * Given an EntryRef, return the AdrBk_Entry that it points to.  It may
  2019.  * already be cached.
  2020.  */
  2021. AdrBk_Entry *
  2022. init_ae_entry(ab, entry)
  2023.     AdrBk    *ab;
  2024.     EntryRef *entry;
  2025. {
  2026.     char *p, *lc;
  2027.     char *buf; /* read entry in here */
  2028.     int   ret, length;
  2029.     int   first_entry = 0;
  2030.     char *addrfield = (char *)NULL;
  2031.     char *addrfield_end;
  2032.     AdrBk_Entry *a = (AdrBk_Entry *)NULL;
  2033.     char  p_msg[800];
  2034.     char *nickname, *fullname, *fcc, *extra;
  2035.     long  offset_of_prev_char;
  2036.     time_t mtime;
  2037.  
  2038.     if(!entry) /* shouldn't ever happen */
  2039.       return(a);
  2040.  
  2041.     /* already cached earlier */
  2042.     if(entry->ae)
  2043.       return(entry->ae);
  2044.  
  2045.     a = adrbk_newentry();
  2046.     entry->ae = a;
  2047.  
  2048.     if(!ab){
  2049.     dprint(2, (debugfile, "init_ae_entry: found trouble: ab is NULL\n"));
  2050.     goto trouble;
  2051.     }
  2052.  
  2053.     length = length_of_entry(ab->fp, entry->offset);
  2054.     if(length <= 0){
  2055.     dprint(2, (debugfile, "init_ae_entry: found trouble: length=%d\n",
  2056.         length));
  2057.     goto trouble;
  2058.     }
  2059.  
  2060.     offset_of_prev_char = entry->offset;
  2061.     if(offset_of_prev_char > 0L){
  2062.     offset_of_prev_char--;
  2063.     length++;
  2064.     }
  2065.     else
  2066.       first_entry++;
  2067.  
  2068.     if(fseek(ab->fp, offset_of_prev_char, 0)){
  2069.     dprint(2, (debugfile,
  2070.         "init_ae_entry: found trouble: fseek to %ld failed\n",
  2071.         offset_of_prev_char));
  2072.     goto trouble;
  2073.     }
  2074.  
  2075.     /* now pointing at the entry (or one before the entry if not first) */
  2076.     buf = (char *)fs_get(length * sizeof(char) + 1);
  2077.     ret = fread(buf, sizeof(char), (unsigned)length, ab->fp);
  2078.     if(ret != length){
  2079.     dprint(2, (debugfile,
  2080.         "init_ae_entry: found trouble: fread returned %d instead of %d\n",
  2081.         ret, length));
  2082.     goto trouble;
  2083.     }
  2084.  
  2085.     buf[length] = '\0';
  2086.     /*
  2087.      * Check to see if things look ok at this offset.
  2088.      */
  2089.     p = buf;
  2090.     if(!first_entry){
  2091.     if(!(*p == '\n' || *p == '\r')){
  2092.         dprint(2, (debugfile,
  2093.            "init_ae_entry: trouble: char before nick at %ld not CR or NL\n",
  2094.         entry->offset));
  2095.         dprint(2, (debugfile, "             : buf = >%s<\n", buf));
  2096.         goto trouble;
  2097.     }
  2098.  
  2099.     p++;
  2100.     }
  2101.  
  2102.     /* done checking for trouble */
  2103.  
  2104.     REPLACE_NEWLINES_WITH_SPACE(p);
  2105.  
  2106.     nickname = p;
  2107.     SKIP_TO_TAB(p);
  2108.     if(!*p){
  2109.     RM_END_SPACE(nickname, p);
  2110.     a->nickname = cpystr(nickname);
  2111.     }
  2112.     else{
  2113.     *p = '\0';
  2114.     RM_END_SPACE(nickname, p);
  2115.     a->nickname = cpystr(nickname);
  2116.     p++;
  2117.     SKIP_SPACE(p);
  2118.     fullname = p;
  2119.     SKIP_TO_TAB(p);
  2120.     if(!*p){
  2121.         RM_END_SPACE(fullname, p);
  2122.         a->fullname = cpystr(fullname);
  2123.     }
  2124.     else{
  2125.         *p = '\0';
  2126.         RM_END_SPACE(fullname, p);
  2127.         a->fullname = cpystr(fullname);
  2128.         p++;
  2129.         SKIP_SPACE(p);
  2130.         addrfield = p;
  2131.         SKIP_TO_TAB(p);
  2132.         if(!*p){
  2133.         RM_END_SPACE(addrfield, p);
  2134.         }
  2135.         else{
  2136.         *p = '\0';
  2137.         RM_END_SPACE(addrfield, p);
  2138.         p++;
  2139.         SKIP_SPACE(p);
  2140.         fcc = p;
  2141.         SKIP_TO_TAB(p);
  2142.         if(!*p){
  2143.             RM_END_SPACE(fcc, p);
  2144.             a->fcc = cpystr(fcc);
  2145.         }
  2146.         else{
  2147.             *p = '\0';
  2148.             RM_END_SPACE(fcc, p);
  2149.             a->fcc = cpystr(fcc);
  2150.             p++;
  2151.             SKIP_SPACE(p);
  2152.             extra = p;
  2153.             p = extra + strlen(extra);
  2154.             RM_END_SPACE(extra, p);
  2155.             a->extra = cpystr(extra);
  2156.         }
  2157.         }
  2158.     }
  2159.     }
  2160.  
  2161.     /* parse addrfield */
  2162.     if(addrfield){
  2163.     if(*addrfield == '('){  /* it's a list */
  2164.         a->tag = List;
  2165.         p = addrfield;
  2166.         addrfield_end = p + strlen(p);
  2167.  
  2168.         /*
  2169.          * Get rid of the parens.
  2170.          * If this isn't true the input file is messed up.
  2171.          */
  2172.         if(p[strlen(p)-1] == ')'){
  2173.         p[strlen(p)-1] = '\0';
  2174.         p++;
  2175.         a->addr.list = parse_addrlist(p);
  2176.         }
  2177.         else{
  2178.         /* put back what was there to start with */
  2179.         *addrfield_end = ')';
  2180.         a->addr.list = (char **)fs_get(sizeof(char *) * 2);
  2181.         a->addr.list[0] = cpystr(addrfield);
  2182.         a->addr.list[1] = NULL;
  2183.         /* just report first error */
  2184.         if(!*p_msg)
  2185.           sprintf(p_msg, "nickname %s: %.500s",
  2186.                 a->nickname, "addressbook entry is corrupt");
  2187.  
  2188.         dprint(1,
  2189.             (debugfile, "parsing error reading addressbook: %s %s\n",
  2190.                    "missing right paren", addrfield));
  2191.         }
  2192.     }
  2193.     else{  /* A plain, single address */
  2194.  
  2195.         a->addr.addr = cpystr(addrfield);
  2196.         a->tag       = Single;
  2197.     }
  2198.     }
  2199.     else{
  2200.     /*
  2201.      * If no addrfield, assume an empty Single.
  2202.      */
  2203.     a->addr.addr = cpystr("");
  2204.     a->tag       = Single;
  2205.     }
  2206.  
  2207.     fs_give((void **)&buf);
  2208.  
  2209.     return(a);
  2210.  
  2211. trouble:
  2212.     /*
  2213.      * Some other process must have changed the on-disk addrbook out from
  2214.      * under us.  Either that, or the hash file must be messed up.  We need
  2215.      * to close down the files and re-open them, else our pointers will
  2216.      * point at crazy places.
  2217.      *
  2218.      * Attempt to verify the change by stat'ing the files.  If mtime didn't
  2219.      * change we'll hope for the best instead of restarting.  We don't want
  2220.      * to get into a restart loop when the file really isn't changing
  2221.      * but the trouble is being triggered for some other reason.
  2222.      *
  2223.      * However, we do want to try to rebuild at least once even if the mtime
  2224.      * looks ok, because somebody may have copied a valid looking .lu file
  2225.      * onto our .lu file (one with the same number of entries).  So we have
  2226.      * this special ad hoc counter (forced_rebuilds) that lets us try to
  2227.      * rebuild a few times regardless of the mtimes.  We also have the
  2228.      * safety net (trouble_rebuilds) to stop us eventually if we get looping
  2229.      * somehow.
  2230.      */
  2231.     dprint(1, (debugfile,
  2232.       "\n\n ADDR    ::: the addressbook file %s and its lookup file %s\n",
  2233.       (ab && ab->filename) ? ab->filename : "?",
  2234.       (ab && ab->hashfile) ? ab->hashfile : "?"));
  2235.     dprint(1, (debugfile,
  2236.       " BOOK    ::: are not consistent with one another.  The lookup\n"));
  2237.     dprint(1, (debugfile,
  2238.       " TROUBLE ::: file may have to be removed and rebuilt.\n"));
  2239.     dprint(1, (debugfile,
  2240.      "         ::: Usually it will fix itself, but if it doesn't, or if it\n"));
  2241.     dprint(1, (debugfile,
  2242.       "         ::: is building temporary lookup files for each user,\n"));
  2243.     dprint(1, (debugfile,
  2244.       "         ::: the sys admin should rebuild it (%s).\n\n",
  2245.       (ab && ab->hashfile) ? ab->hashfile : "?"));
  2246.     if(((ab && ab->last_change_we_know_about != (time_t)(-1) &&
  2247.      (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
  2248.      ab->last_change_we_know_about != mtime) ||
  2249.     forced_rebuilds < MAX_FORCED_REBUILDS) &&
  2250.          trouble_rebuilds < MAX_TROUBLE_REBUILDS){
  2251.  
  2252.     q_status_message(SM_ORDER | SM_DING, 5, 5,
  2253.        "Addrbook has been changed by another process, need to re-sync...");
  2254.     if(writing){
  2255.        writing = 0;
  2256.        q_status_message(SM_ORDER, 3, 5,
  2257.            "Aborting our change to avoid damage...");
  2258.     }
  2259.  
  2260.     dprint(1, (debugfile,
  2261.         "addrbook %s changed while we had it open, longjmp\n",
  2262.         (ab && ab->filename) ? ab->filename : "?"));
  2263.         if(ab && ab->last_change_we_know_about == (time_t)(-1) ||
  2264.        mtime == (time_t)(-1) ||
  2265.        ab->last_change_we_know_about == mtime)
  2266.         forced_rebuilds++;
  2267.  
  2268.     trouble_rebuilds++;
  2269.     /* jump back to a safe place */
  2270.     trouble_filename = (ab && ab->orig_filename)
  2271.                 ? cpystr(ab->orig_filename)
  2272.                 : cpystr("");
  2273.     longjmp(addrbook_changed_unexpectedly, 1);
  2274.     /*NOTREACHED*/
  2275.     }
  2276.  
  2277.     dprint(1, (debugfile,
  2278.     "addrbook trouble (%s), but we're returning null nickname\n",
  2279.     (ab && ab->filename) ? ab->filename : "?"));
  2280.     dprint(1, (debugfile,
  2281.     "Advised user to remove %s and restart pine\n",
  2282.     (ab && ab->hashfile) ? ab->hashfile : "?"));
  2283.     dprint(1, (debugfile,
  2284.     "If %s not owned by user, sys admin may have to rebuild it\n",
  2285.     (ab && ab->hashfile) ? ab->hashfile : "?"));
  2286.     if(trouble_rebuilds++ < MAX_TROUBLE_REBUILDS + 3)
  2287.       q_status_message1(SM_ORDER, 3, 10,
  2288.     "Lookup file %s inconsistent...remove it and restart Pine",
  2289.     (ab && ab->hashfile) ? ((lc=last_cmpnt(ab->hashfile))
  2290.                      ? lc : ab->hashfile)
  2291.                  : "?");
  2292.  
  2293.     fs_give((void **)&a);
  2294.     entry->ae = (AdrBk_Entry *)NULL;
  2295.     return((AdrBk_Entry *)NULL);
  2296. }
  2297.  
  2298.  
  2299. /*
  2300.  * Parses a string of comma-separated addresses or nicknames into an
  2301.  * array.
  2302.  *
  2303.  * Returns an allocated, null-terminated list, or NULL.
  2304.  */
  2305. char **
  2306. parse_addrlist(addrfield)
  2307.     char *addrfield;
  2308. {
  2309. #define LISTCHUNK  500   /* Alloc this many addresses for list at a time */
  2310.     char **al, **ad;
  2311.     char *next_addr, *cur_addr, *p, *q;
  2312.     int slots = LISTCHUNK;
  2313.  
  2314.     if(!addrfield)
  2315.       return((char **)NULL);
  2316.  
  2317.     /* allocate first chunk */
  2318.     slots = LISTCHUNK;
  2319.     al    = (char **)fs_get(sizeof(char *) * (slots+1));
  2320.     ad    = al;
  2321.  
  2322.     p = addrfield;
  2323.  
  2324.     /* skip any leading whitespace */
  2325.     for(q = p; *q && *q == SPACE; q++)
  2326.       ;/* do nothing */
  2327.  
  2328.     next_addr = (*q) ? q : NULL;
  2329.  
  2330.     /* Loop adding each address in list to array al */
  2331.     for(cur_addr = next_addr; cur_addr; cur_addr = next_addr){
  2332.  
  2333.     next_addr = skip_to_next_addr(cur_addr);
  2334.  
  2335.     q = cur_addr;
  2336.     SKIP_SPACE(q);
  2337.  
  2338.     /* allocate more space */
  2339.     if((ad-al) >= slots){
  2340.         slots += LISTCHUNK;
  2341.         fs_resize((void **)&al, sizeof(char *) * (slots+1));
  2342.         ad = al + slots - LISTCHUNK;
  2343.     }
  2344.  
  2345.     if(*q)
  2346.       *ad++ = cpystr(q);
  2347.     }
  2348.  
  2349.     *ad++ = NULL;
  2350.  
  2351.     /* free up any excess we've allocated */
  2352.     fs_resize((void **)&al, sizeof(char *) * (ad - al));
  2353.     return(al);
  2354. }
  2355.  
  2356.  
  2357. /*
  2358.  * returns length of the address book entry starting at offset
  2359.  */
  2360. int
  2361. length_of_entry(fp, offset)
  2362.     FILE *fp;
  2363.     long  offset;
  2364. {
  2365.     char line[MAXLINE+1];
  2366.     char *p;
  2367.     long new_offset = offset;
  2368.  
  2369.     errno = 0;
  2370.     line[0] = '\0';
  2371.     if(!fp)
  2372.       return -1;
  2373.  
  2374.     if(fseek(fp, offset, 0))
  2375.       return -1;
  2376.  
  2377.     clearerr(fp);
  2378.     errno = 0;
  2379.     p = fgets(line, MAXLINE+1, fp);
  2380.  
  2381.     do{
  2382.     new_offset = ftell(fp);
  2383.     p          = fgets(line, MAXLINE+1, fp);
  2384.     
  2385.     }while(p && *p == SPACE);
  2386.     
  2387.     if(new_offset <= offset){
  2388.     dprint(2,(debugfile,"length_of_entry: trouble: return length=%ld, %s\n",
  2389.         new_offset-offset, error_description(errno)));
  2390.     dprint(2, (debugfile, "    offset=%ld, p=%s\n", offset,
  2391.         p ? (*p ? p : "<empty>") : "<null>"));
  2392.     dprint(2, (debugfile, "    line=%s\n", *line ? line : "<empty>"));
  2393.     if(p == NULL)
  2394.       dprint(2, (debugfile, "    ferror(fp)=%d, feof(fp)=%d\n",
  2395.         ferror(fp), feof(fp)));
  2396.     }
  2397.  
  2398.     return((int)(new_offset - offset));
  2399. }
  2400.  
  2401.  
  2402. /*
  2403.  * Args  cur -- pointer to the start of the current addr in list.
  2404.  *
  2405.  * Returns a pointer to the start of the next addr or NULL if there are
  2406.  * no more addrs.
  2407.  *
  2408.  * Side effect: current addr has trailing white space removed
  2409.  * and is null terminated.
  2410.  */
  2411. char *
  2412. skip_to_next_addr(cur)
  2413.     char *cur;
  2414. {
  2415.     register char *p,
  2416.           *q;
  2417.     char          *ret_pointer;
  2418.     int in_quotes  = 0,
  2419.         in_comment = 0;
  2420.     char prev_char = '\0';
  2421.  
  2422.     /*
  2423.      * Find delimiting comma or end.
  2424.      * Quoted commas and commented commas don't count.
  2425.      */
  2426.     for(q = cur; *q; q++){
  2427.     switch(*q){
  2428.       case COMMA:
  2429.         if(!in_quotes && !in_comment)
  2430.           goto found_comma;
  2431.         break;
  2432.  
  2433.       case LPAREN:
  2434.         if(!in_quotes && !in_comment)
  2435.           in_comment = 1;
  2436.         break;
  2437.  
  2438.       case RPAREN:
  2439.         if(in_comment && prev_char != BSLASH)
  2440.           in_comment = 0;
  2441.         break;
  2442.  
  2443.       case QUOTE:
  2444.         if(in_quotes && prev_char != BSLASH)
  2445.           in_quotes = 0;
  2446.         else if(!in_quotes && !in_comment)
  2447.           in_quotes = 1;
  2448.         break;
  2449.  
  2450.       default:
  2451.         break;
  2452.     }
  2453.  
  2454.     prev_char = *q;
  2455.     }
  2456.     
  2457. found_comma:
  2458.     if(*q){  /* trailing comma case */
  2459.     *q = '\0';
  2460.     ret_pointer = q + 1;
  2461.     }
  2462.     else
  2463.       ret_pointer = NULL;  /* no more addrs after cur */
  2464.  
  2465.     /* remove trailing white space from cur */
  2466.     for(p = q - 1; p >= cur && isspace((unsigned char)*p); p--)
  2467.       *p = '\0';
  2468.     
  2469.     return(ret_pointer);
  2470. }
  2471.  
  2472.  
  2473. /*
  2474.  * Return the size of the address book 
  2475.  */
  2476. adrbk_cntr_t
  2477. adrbk_count(ab)
  2478.     AdrBk *ab;
  2479. {
  2480.     return(ab ? ab->count : (adrbk_cntr_t)0);
  2481. }
  2482.  
  2483.  
  2484. /*
  2485.  * Get the ae that has index number "entry_num" in "handling" mode.
  2486.  *
  2487.  * Handling - Normal - Means it may be deleted from cache out from under us.
  2488.  *            Lock   - Means it may not be deleted from the cache (so that
  2489.  *                     we can continue to use pointers to it) until we
  2490.  *                     Unlock it explicitly or until adrbk_write is called.
  2491.  *            Unlock - Usually just used to unlock a locked entry.  Adrbk_write
  2492.  *                     also does this unlocking.  It also returns the ae.
  2493.  *   Returns NULL if entry_num is out of range.  Otherwise, it returns an ae.
  2494.  *   It never returns NULL when it should return an ae.  Instead, if it can't
  2495.  *   figure out what the entry is, it returns an empty, Single entry.  This
  2496.  *   means that the users of adrbk_get_ae don't have to check for NULL.
  2497.  *   Note, however, that it is possible that a caller will get back an empty
  2498.  *   Single while expecting a List.
  2499.  */
  2500. AdrBk_Entry *
  2501. adrbk_get_ae(ab, entry_num, handling)
  2502.     AdrBk    *ab;
  2503.     a_c_arg_t entry_num;
  2504.     Handling  handling;
  2505. {
  2506.     EntryRef *entry = (EntryRef *)NULL;
  2507.     AdrBk_Entry *ae = (AdrBk_Entry *)NULL;
  2508.  
  2509.     dprint(9, (debugfile, "- adrbk_get_ae -\n"));
  2510.  
  2511.     if(ab && entry_num >= (a_c_arg_t)ab->count)
  2512.       return(ae);
  2513.  
  2514.     entry = adrbk_get_entryref(ab, entry_num, handling);
  2515.     if(entry != NULL && entry->uid_nick != NO_UID)
  2516.       ae = init_ae_entry(ab, entry);
  2517.  
  2518. #ifdef DEBUG
  2519.     if(ae == (AdrBk_Entry *)NULL){
  2520.     dprint(2, (debugfile, "adrbk_get_ae (%s): returning NULL!\n",
  2521.         ab->filename));
  2522.     dprint(2, (debugfile, "   : count %ld l_c_w_k_a %ld cur_time %lu\n",
  2523.         (long)ab->count, (long)ab->last_change_we_know_about,
  2524.         (unsigned long)get_adj_time()));
  2525.     dprint(2, (debugfile, "   : requested entry_num %ld\n",
  2526.         (long)entry_num));
  2527.     if(entry == NULL){
  2528.         dprint(2,
  2529.          (debugfile, "   : got back NULL entry from adrbk_get_entryref\n"));
  2530.     }
  2531.     else{
  2532.         dprint(2, (debugfile, "   : uid_nick %ld uid_addr %ld offset\n",
  2533.         (long)entry->uid_nick, (long)entry->uid_addr, entry->offset));
  2534.     }
  2535.     }
  2536. #endif /* DEBUG */
  2537.  
  2538.     /* This assigns a non-null value to ae. */
  2539.     if(ae == (AdrBk_Entry *)NULL){
  2540.     ae = adrbk_newentry();
  2541.     ae->tag       = Single;
  2542.     ae->nickname  = cpystr("");
  2543.     ae->addr.addr = cpystr("");
  2544.     if(entry)
  2545.       entry->ae = ae;
  2546.     /* else, memory leak that shouldn't happen often */
  2547.     }
  2548.  
  2549.     return(ae);
  2550. }
  2551.  
  2552.  
  2553. /*
  2554.  * Look up an entry in the address book given a nickname
  2555.  *
  2556.  * Args: ab       -- the address book
  2557.  *       nickname -- nickname to match
  2558.  *      entry_num -- if matched, return entry_num of match here
  2559.  *
  2560.  * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
  2561.  *
  2562.  * Lookups usually need to be recursive in case the address
  2563.  * book references itself.  This is left to the next level up.
  2564.  * adrbk_clearrefs() is provided to clear all the reference tags in
  2565.  * the address book for loop detetction.
  2566.  * When there are duplicates of the same nickname we return the first.
  2567.  * This can only happen if addrbook was edited externally.
  2568.  */
  2569. AdrBk_Entry *
  2570. adrbk_lookup_by_nick(ab, nickname, entry_num)
  2571.     AdrBk *ab;
  2572.     char  *nickname;
  2573.     adrbk_cntr_t *entry_num;
  2574. {
  2575.     adrbk_cntr_t hash;
  2576.     adrbk_uid_t uid;
  2577.     adrbk_cntr_t ind, last_ind;
  2578.     EntryRef *entry, *last_one;
  2579.     AdrBk_Entry *ae;
  2580.  
  2581.     dprint(2, (debugfile, "- adrbk_lookup_by_nick(%s) (in %s) -\n", nickname,
  2582.     (ab && ab->filename) ? ab->filename : "?"));
  2583.  
  2584.     if(!ab || !nickname || !nickname[0])
  2585.       return NULL;
  2586.  
  2587.     hash = ab_hash(nickname, (a_c_arg_t)ab->htable_size);
  2588.     uid  = ab_uid(nickname);
  2589.  
  2590.     last_one = (EntryRef *)NULL;
  2591.  
  2592.     for(ind = ab->hash_by_nick->harray[hash];
  2593.     ind != NO_NEXT &&
  2594.             (entry = adrbk_get_entryref(ab, (a_c_arg_t)ind, Normal));
  2595.     ind = entry->next_nick){
  2596.  
  2597.     if(entry->uid_nick == uid){
  2598.         ae = adrbk_get_ae(ab, (a_c_arg_t)ind, Normal);
  2599.         /*
  2600.          * Guard against the unlikely case where two nicknames have the
  2601.          * same hash and uid, but are actually different.
  2602.          */
  2603.         if(strucmp(ae->nickname, nickname) == 0){
  2604.         last_one = entry;
  2605.         last_ind = ind;
  2606.         }
  2607.     }
  2608.     }
  2609.  
  2610.     /* no such nickname */
  2611.     if(last_one == (EntryRef *)NULL)
  2612.       return((AdrBk_Entry *)NULL);
  2613.  
  2614.     if(entry_num)
  2615.       *entry_num = last_ind;
  2616.  
  2617.     return(adrbk_get_ae(ab, (a_c_arg_t)last_ind, Normal));
  2618. }
  2619.  
  2620.  
  2621. /*
  2622.  * Look up an entry in the address book given an address
  2623.  *
  2624.  * Args: ab       -- the address book
  2625.  *       address  -- address to match
  2626.  *      entry_num -- if matched, return entry_num of match here
  2627.  *
  2628.  * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
  2629.  *
  2630.  * Note:  When there are multiple occurrences of an address in an addressbook,
  2631.  * which there will be if more than one nickname points to same address, then
  2632.  * we want this to match the first occurrence so that the fcc you get will
  2633.  * be predictable.  Because of the way the hash table is built (and needs to
  2634.  * be built) we need to look for the last occurrence of uid within the list
  2635.  * a hash table entry points to instead of the first occurrence.
  2636.  */
  2637. AdrBk_Entry *
  2638. adrbk_lookup_by_addr(ab, address, entry_num)
  2639.     AdrBk *ab;
  2640.     char  *address;
  2641.     adrbk_cntr_t *entry_num;
  2642. {
  2643.     adrbk_cntr_t hash;
  2644.     adrbk_uid_t uid;
  2645.     adrbk_cntr_t ind, last_ind;
  2646.     EntryRef *entry, *last_one;
  2647.  
  2648.     dprint(2, (debugfile, "- adrbk_lookup_by_addr(%s) (in %s) -\n", address,
  2649.     (ab && ab->filename) ? ab->filename : "?"));
  2650.  
  2651.     if(!ab || !address || !address[0])
  2652.       return NULL;
  2653.  
  2654.     hash = ab_hash_addr(address, (a_c_arg_t)ab->htable_size);
  2655.     uid  = ab_uid_addr(address);
  2656.  
  2657.     last_one = (EntryRef *)NULL;
  2658.  
  2659.     for(ind = ab->hash_by_addr->harray[hash];
  2660.     ind != NO_NEXT &&
  2661.             (entry = adrbk_get_entryref(ab, (a_c_arg_t)ind, Normal));
  2662.     ind = entry->next_addr){
  2663.  
  2664.     if(entry->uid_addr == uid){
  2665.         last_one = entry;
  2666.         last_ind = ind;
  2667.     }
  2668.     }
  2669.  
  2670.     /* no such address */
  2671.     if(last_one == (EntryRef *)NULL)
  2672.       return((AdrBk_Entry *)NULL);
  2673.  
  2674.     if(entry_num)
  2675.       *entry_num = last_ind;
  2676.  
  2677.     return(adrbk_get_ae(ab, (a_c_arg_t)last_ind, Normal));
  2678. }
  2679.  
  2680.  
  2681. /*
  2682.  * Format a full name.
  2683.  *
  2684.  * Args: fullname -- full name out of address book for formatting
  2685.  *
  2686.  * Result:  Returns pointer to static internal buffer containing name
  2687.  * formatted for a mail header.
  2688.  *
  2689.  * We need this because we store full names as Last, First.
  2690.  * If the name has no comma, then no change is made.
  2691.  * Otherwise the text before the first comma is moved to the end and
  2692.  * the comma is deleted.
  2693.  */
  2694. char *
  2695. adrbk_formatname(fullname)
  2696.     char *fullname;
  2697. {
  2698.     char       *comma, *p, *charset = NULL;
  2699.     char        buf[MAX_FULLNAME + 10];
  2700.     static char new_name[MAX_FULLNAME];
  2701.     int         need_to_encode = 0;
  2702.  
  2703.     /*
  2704.      * It's difficult to find comma when it is encoded, so decode it
  2705.      * and search for comma.  Note, we can't handle a fullname with
  2706.      * multiple charsets in it.
  2707.      */
  2708.     p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,fullname,&charset);
  2709.     if(p == fullname){
  2710.     if(charset)
  2711.       fs_give((void **)&charset);
  2712.  
  2713.     charset = NULL;
  2714.     }
  2715.     else
  2716.       need_to_encode++;
  2717.  
  2718.     fullname = p;  /* overloading fullname, now decoded */
  2719.  
  2720.     if(fullname[0] != '"'  && (comma = strindex(fullname, ',')) != NULL){
  2721.         int last_name_len = comma - fullname;
  2722.         comma++;
  2723.         while(*comma && isspace((unsigned char)*comma))
  2724.       comma++;
  2725.  
  2726.         strcpy(new_name, comma);
  2727.         strcat(new_name, " ");
  2728.         strncat(new_name, fullname, last_name_len); 
  2729.     }
  2730.     else
  2731.       strcpy(new_name, fullname);
  2732.     
  2733.     if(need_to_encode){
  2734.     char *s;
  2735.  
  2736.     /* re-encode in original charset */
  2737.     s = rfc1522_encode(buf, (unsigned char *)new_name,
  2738.         charset ? charset : ps_global->VAR_CHAR_SET);
  2739.     if(s != new_name)
  2740.       strncpy(new_name, s, MAX_FULLNAME);
  2741.     
  2742.     new_name[MAX_FULLNAME-1] = '\0';
  2743.     if(charset)
  2744.       fs_give((void **)&charset);
  2745.     }
  2746.  
  2747.     return(new_name);
  2748. }
  2749.  
  2750.  
  2751. /*
  2752.  * Clear reference flags in preparation for a recursive lookup.
  2753.  *
  2754.  * For loop detection during address book look up.  This clears all the 
  2755.  * referenced flags, then as the lookup proceeds the referenced flags can 
  2756.  * be checked and set.
  2757.  */
  2758. void
  2759. adrbk_clearrefs(ab)
  2760. AdrBk *ab;
  2761.     {
  2762.     dprint(9, (debugfile, "- adrbk_clearrefs -\n"));
  2763.  
  2764.     if(!ab)
  2765.       return;
  2766.  
  2767.     /*
  2768.      * We only have to clear the references in cached ae's, since newly
  2769.      * created ae's start out with cleared references.  This should speed
  2770.      * things up considerably for large addrbooks.
  2771.      */
  2772.     clearrefs_in_cached_aes(ab);
  2773. }
  2774.  
  2775.  
  2776. /*
  2777.  *  Allocate a new AdrBk_Entry
  2778.  */
  2779. AdrBk_Entry *
  2780. adrbk_newentry()
  2781. {
  2782.     AdrBk_Entry *a;
  2783.  
  2784.     a = (AdrBk_Entry *)fs_get(sizeof(AdrBk_Entry));
  2785.     a->nickname    = empty;
  2786.     a->fullname    = empty;
  2787.     a->addr.addr   = empty;
  2788.     a->fcc         = empty;
  2789.     a->extra       = empty;
  2790.     a->tag         = NotSet;
  2791.     a->referenced  = 0;
  2792.  
  2793.     return(a);
  2794. }
  2795.  
  2796.  
  2797. AdrBk_Entry *
  2798. copy_ae(src)
  2799.     AdrBk_Entry *src;
  2800. {
  2801.     AdrBk_Entry *a;
  2802.  
  2803.     a = adrbk_newentry();
  2804.     a->tag = src->tag;
  2805.     a->nickname = cpystr(src->nickname ? src->nickname : "");
  2806.     a->fullname = cpystr(src->fullname ? src->fullname : "");
  2807.     a->fcc      = cpystr(src->fcc ? src->fcc : "");
  2808.     a->extra    = cpystr(src->extra ? src->extra : "");
  2809.     if(a->tag == Single)
  2810.       a->addr.addr = cpystr(src->addr.addr ? src->addr.addr : "");
  2811.     else if(a->tag == List){
  2812.     char **p;
  2813.     int    i, n;
  2814.  
  2815.     /* count list */
  2816.     for(p = src->addr.list; p && *p; p++)
  2817.       ;/* do nothing */
  2818.     
  2819.     if(p == NULL)
  2820.       n = 0;
  2821.     else
  2822.       n = p - src->addr.list;
  2823.  
  2824.     a->addr.list = (char **)fs_get((n+1) * sizeof(char *));
  2825.     for(i = 0; i < n; i++)
  2826.       a->addr.list[i] = cpystr(src->addr.list[i]);
  2827.     
  2828.     a->addr.list[n] = NULL;
  2829.     }
  2830.  
  2831.     return(a);
  2832. }
  2833.  
  2834.  
  2835.  
  2836. /*
  2837.  * Add an entry to the address book, or modify an existing entry
  2838.  *
  2839.  * Args: ab       -- address book to add to
  2840.  *  old_entry_num -- the entry we want to modify.  If this is NO_NEXT, then
  2841.  *                    we look up the nickname passed in to see if that's the
  2842.  *                    entry to modify, else it is a new entry.
  2843.  *       nickname -- the nickname for new entry
  2844.  *       fullname -- the fullname for new entry
  2845.  *       address  -- the address for new entry
  2846.  *       fcc      -- the fcc for new entry
  2847.  *       extra    -- the extra field for new entry
  2848.  *       tag      -- the type of new entry
  2849.  *  new_entry_num -- return entry_num of new or modified entry here
  2850.  *  resort_happened -- means that more than just the current entry changed,
  2851.  *                     either something was added or order was changed
  2852.  *
  2853.  * Result: return code:  0 all went well
  2854.  *                      -2 error writing address book, check errno
  2855.  *                -3 no modification, the tag given didn't match
  2856.  *                         existing tag
  2857.  *                      -4 tabs are in one of the fields passed in
  2858.  *
  2859.  * If the nickname exists in the address book already, the operation is
  2860.  * considered a modification even if the case does not match exactly,
  2861.  * otherwise it is an add.  The entry the operation occurs on is returned
  2862.  * in new.  All fields are set to those passed in; that is, passing in NULL
  2863.  * even on a modification will set those fields to NULL as opposed to leaving
  2864.  * them unchanged.  It is acceptable to pass in the current strings
  2865.  * in the entry in the case of modification.  For address lists, the
  2866.  * structure passed in is what is used, so the storage has to all have
  2867.  * come from malloc or fs_get().  If the pointer passed in is the same as
  2868.  * the current field, no change is made.
  2869.  */
  2870. int
  2871. adrbk_add(ab, old_entry_num, nickname, fullname, address, fcc, extra, tag,
  2872.       new_entry_num, resort_happened)
  2873.     AdrBk        *ab;
  2874.     a_c_arg_t     old_entry_num;
  2875.     char         *nickname,
  2876.          *fullname,
  2877.          *address, /* address can be char **, too */
  2878.          *fcc,
  2879.          *extra;
  2880.     Tag           tag;
  2881.     adrbk_cntr_t *new_entry_num;
  2882.     int          *resort_happened;
  2883. {
  2884.     AdrBk_Entry *a;
  2885.     AdrBk_Entry *ae;
  2886.     adrbk_cntr_t old_enum;
  2887.     adrbk_cntr_t new_enum;
  2888.     int (*cmp_func)();
  2889.     int retval;
  2890.     int need_write = 0;
  2891.  
  2892.     if(!ab)
  2893.       return -2;
  2894.  
  2895.     /* ---- Make sure there are no tabs in the stuff to add ------*/
  2896.     if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
  2897.        (fullname != NULL && strindex(fullname, TAB) != NULL) ||
  2898.        (fcc != NULL && strindex(fcc, TAB) != NULL) ||
  2899.        (tag == Single && address != NULL && strindex(address, TAB) != NULL))
  2900.         return -4;
  2901.  
  2902.     /*
  2903.      * Are we adding or updating ?
  2904.      *
  2905.      * If old_entry_num was passed in, we're updating that.  If nickname
  2906.      * already exists, we're updating that entry.  Otherwise, this is an add.
  2907.      */
  2908.     if((adrbk_cntr_t)old_entry_num != NO_NEXT){
  2909.     ae = adrbk_get_ae(ab, old_entry_num, Normal);
  2910.     if(ae)
  2911.       old_enum = (adrbk_cntr_t)old_entry_num;
  2912.     }
  2913.     else
  2914.       ae = adrbk_lookup_by_nick(ab, nickname, &old_enum);
  2915.  
  2916.     if(ae == NULL){  /*----- adding a new entry ----*/
  2917.  
  2918.         ae            = adrbk_newentry();
  2919.         ae->nickname  = nickname ? cpystr(nickname) : nickname;
  2920.         ae->fullname  = fullname ? cpystr(fullname) : fullname;
  2921.         ae->fcc       = fcc      ? cpystr(fcc)      : fcc;
  2922.         ae->extra     = extra    ? cpystr(extra)    : extra;
  2923.     ae->tag       = tag;
  2924.  
  2925.     if(tag == Single)
  2926.           ae->addr.addr = cpystr(address);
  2927.     else
  2928.       ae->addr.list = (char **)NULL;
  2929.  
  2930.     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  2931.                         cmp_ae_by_full_lists_last :
  2932.            (ab->sort_rule == AB_SORT_RULE_FULL) ?
  2933.                         cmp_ae_by_full :
  2934.            (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  2935.                         cmp_ae_by_nick_lists_last :
  2936.         /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  2937.                         cmp_ae_by_nick;
  2938.  
  2939.     if(ab->sort_rule == AB_SORT_RULE_NONE)  /* put it last */
  2940.       new_enum = ab->count;
  2941.     else  /* Find slot for it */
  2942.       for(new_enum = 0, a = adrbk_get_ae(ab, (a_c_arg_t)new_enum, Normal);
  2943.           a != (AdrBk_Entry *)NULL;
  2944.           a = adrbk_get_ae(ab, (a_c_arg_t)(++new_enum), Normal)){
  2945.             if((*cmp_func)((QSType *)&a, (QSType *)&ae) >= 0)
  2946.             break;
  2947.       }
  2948.     /* Insert ae before entry new_enum. */
  2949.     set_inserted_entryref(ab, (a_c_arg_t)new_enum, ae);
  2950.  
  2951.         /*---- return in pointer if requested -----*/
  2952.         if(new_entry_num)
  2953.       *new_entry_num = new_enum;
  2954.  
  2955.         if(resort_happened)
  2956.       *resort_happened = 1;
  2957.  
  2958.     retval = adrbk_write(ab, NULL, 0);
  2959.     if(retval == 0 && F_OFF(F_EXPANDED_DISTLISTS,ps_global))
  2960.       exp_add_nth(ab->exp, (a_c_arg_t)new_enum);
  2961.     }
  2962.     else{
  2963.         /*----- Updating an existing entry ----*/
  2964.  
  2965.     if(ae->tag != tag)
  2966.       return -3;
  2967.  
  2968.     /* Lock this entry in the entryref cache */
  2969.     (void)adrbk_get_entryref(ab, (a_c_arg_t)old_enum, Lock);
  2970.  
  2971.         /*
  2972.      * Instead of just freeing and reallocating here we attempt to reuse
  2973.      * the space that was already allocated if possible.
  2974.      */
  2975.     if(ae->nickname != nickname
  2976.        && ae->nickname != NULL
  2977.        && nickname != NULL
  2978.        && strcmp(nickname, ae->nickname) != 0){
  2979.         need_write++;
  2980.         /* can use already alloc'd space */
  2981.             if(ae->nickname != NULL && nickname != NULL &&
  2982.            strlen(nickname) <= strlen(ae->nickname)){
  2983.  
  2984.                 strcpy(ae->nickname, nickname);
  2985.             }
  2986.         else{
  2987.                 if(ae->nickname != NULL && ae->nickname != empty)
  2988.                   fs_give((void **)&ae->nickname);
  2989.  
  2990.                 ae->nickname = nickname ? cpystr(nickname) : nickname;
  2991.         }
  2992.         }
  2993.  
  2994.     if(ae->fullname != fullname
  2995.        && ae->fullname != NULL
  2996.        && fullname != NULL
  2997.        && strcmp(fullname, ae->fullname) != 0){
  2998.         need_write++;
  2999.             if(ae->fullname != NULL && fullname != NULL &&
  3000.            strlen(fullname) <= strlen(ae->fullname)){
  3001.  
  3002.                 strcpy(ae->fullname, fullname);
  3003.             }
  3004.         else{
  3005.                 if(ae->fullname != NULL && ae->fullname != empty)
  3006.                   fs_give((void **)&ae->fullname);
  3007.  
  3008.                 ae->fullname = fullname ? cpystr(fullname) : fullname;
  3009.         }
  3010.         }
  3011.  
  3012.     if(ae->fcc != fcc
  3013.        && ae->fcc != NULL
  3014.        && fcc != NULL
  3015.        && strcmp(fcc, ae->fcc) != 0){
  3016.         need_write++;
  3017.             if(ae->fcc != NULL && fcc != NULL &&
  3018.            strlen(fcc) <= strlen(ae->fcc)){
  3019.  
  3020.                 strcpy(ae->fcc, fcc);
  3021.             }
  3022.         else{
  3023.                 if(ae->fcc != NULL && ae->fcc != empty)
  3024.                   fs_give((void **)&ae->fcc);
  3025.  
  3026.                 ae->fcc = fcc ? cpystr(fcc) : fcc;
  3027.         }
  3028.         }
  3029.  
  3030.     if(ae->extra != extra
  3031.        && ae->extra != NULL
  3032.        && extra != NULL
  3033.        && strcmp(extra, ae->extra) != 0){
  3034.         need_write++;
  3035.             if(ae->extra != NULL && extra != NULL &&
  3036.            strlen(extra) <= strlen(ae->extra)){
  3037.  
  3038.                 strcpy(ae->extra, extra);
  3039.             }
  3040.         else{
  3041.                 if(ae->extra != NULL && ae->extra != empty)
  3042.                   fs_give((void **)&ae->extra);
  3043.  
  3044.                 ae->extra = extra ? cpystr(extra) : extra;
  3045.         }
  3046.         }
  3047.  
  3048.     if(tag == Single){
  3049.             /*---- Single ----*/
  3050.         if(ae->addr.addr != address
  3051.            && ae->addr.addr != NULL
  3052.            && address != NULL
  3053.            && strcmp(address, ae->addr.addr) != 0){
  3054.         need_write++;
  3055.         if(ae->addr.addr != NULL && address != NULL &&
  3056.            strlen(address) <= strlen(ae->addr.addr)){
  3057.  
  3058.             strcpy(ae->addr.addr, address);
  3059.         }
  3060.         else{
  3061.             if(ae->addr.addr != NULL && ae->addr.addr != empty)
  3062.               fs_give((void **)&ae->addr.addr);
  3063.  
  3064.             ae->addr.addr = address ? cpystr(address) : address;
  3065.         }
  3066.         }
  3067.     }
  3068.     else{
  3069.             /*---- List -----*/
  3070.             /*
  3071.          * We don't mess with lists here.
  3072.          * The caller has to do it with adrbk_listadd().
  3073.          */
  3074.         ;/* do nothing */
  3075.     }
  3076.  
  3077.  
  3078.         /*---------- Make sure it's still in order ---------*/
  3079.     /*
  3080.      * old_enum is where ae is currently located
  3081.      * put it where it belongs
  3082.      */
  3083.     if(need_write)
  3084.       new_enum = re_sort_particular_entryref(ab, (a_c_arg_t)old_enum);
  3085.     else
  3086.       new_enum = old_enum;
  3087.  
  3088.         /*---- return in pointer if requested -----*/
  3089.         if(new_entry_num)
  3090.       *new_entry_num = new_enum;
  3091.  
  3092.         if(resort_happened)
  3093.       *resort_happened = (old_enum != new_enum);
  3094.  
  3095.     if(need_write)
  3096.       retval = adrbk_write(ab, NULL, 0);
  3097.     else{
  3098.         (void)adrbk_get_entryref(ab, (a_c_arg_t)old_enum, Unlock);
  3099.         retval = 0;
  3100.     }
  3101.  
  3102.     /*
  3103.      * If it got re-sorted we just throw in the towel and unexpand
  3104.      * all the lists.  Maybe someday we'll fix it to try to track
  3105.      * the numbers of the expanded lists.
  3106.      */
  3107.     if(old_enum != new_enum && retval == 0)
  3108.       exp_free(ab->exp);
  3109.     }
  3110.  
  3111.     return(retval);
  3112. }
  3113.  
  3114.  
  3115. /*
  3116.  * The entire address book is assumed sorted correctly except perhaps for
  3117.  * entry number cur.  Put it in the correct place.  Return the new entry
  3118.  * number for cur.
  3119.  */
  3120. adrbk_cntr_t
  3121. re_sort_particular_entryref(ab, cur)
  3122.     AdrBk *ab;
  3123.     a_c_arg_t cur;
  3124. {
  3125.     AdrBk_Entry  *ae_cur, *ae_prev, *ae_next, *ae_small_enough, *ae_big_enough;
  3126.     long small_enough;
  3127.     adrbk_cntr_t big_enough;
  3128.     adrbk_cntr_t new_entry_num;
  3129.     int (*cmp_func)();
  3130.  
  3131.     cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  3132.                         cmp_ae_by_full_lists_last :
  3133.            (ab->sort_rule == AB_SORT_RULE_FULL) ?
  3134.                         cmp_ae_by_full :
  3135.            (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  3136.                         cmp_ae_by_nick_lists_last :
  3137.         /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  3138.                         cmp_ae_by_nick;
  3139.  
  3140.     new_entry_num = (adrbk_cntr_t)cur;
  3141.  
  3142.     if(ab->sort_rule == AB_SORT_RULE_NONE)
  3143.       return(new_entry_num);
  3144.  
  3145.     ae_cur = adrbk_get_ae(ab, cur, Lock);
  3146.  
  3147.     if(cur > 0)
  3148.       ae_prev  = adrbk_get_ae(ab, cur - 1, Lock);
  3149.  
  3150.     if(cur < ab->count -1)
  3151.       ae_next  = adrbk_get_ae(ab, cur + 1, Lock);
  3152.  
  3153.     /*
  3154.      * A possible optimization here would be to implement some sort of
  3155.      * binary search to find where it goes instead of stepping through the
  3156.      * entries one at a time.  That way, it might be faster, and, we wouldn't
  3157.      * have to page in as much of the entryref array.  Another optimization
  3158.      * is a way to access cached stuff only, so we could look through all
  3159.      * the cached stuff first before looking at stuff on disk.
  3160.      */
  3161.     if(cur > 0 &&
  3162.        (*cmp_func)((QSType *)&ae_cur,(QSType *)&ae_prev) < 0){
  3163.     /*--- Out of order, needs to be moved up ----*/
  3164.     for(small_enough = (long)cur - 2; small_enough >= 0L; small_enough--){
  3165.       ae_small_enough = adrbk_get_ae(ab,(a_c_arg_t)small_enough,Normal);
  3166.       if((*cmp_func)((QSType *)&ae_cur, (QSType *)&ae_small_enough) >= 0)
  3167.         break;
  3168.     }
  3169.     new_entry_num = (adrbk_cntr_t)(small_enough + 1L);
  3170.     set_moved_entryref(cur, (a_c_arg_t)new_entry_num);
  3171.     }
  3172.     else if(cur < ab->count - 1 &&
  3173.     (*cmp_func)((QSType *)&ae_cur, (QSType *)&ae_next) > 0){
  3174.     /*---- Out of order needs, to be moved towards end of list ----*/
  3175.     for(big_enough = (adrbk_cntr_t)(cur + 2);
  3176.         big_enough < ab->count;
  3177.         big_enough++){
  3178.       ae_big_enough = adrbk_get_ae(ab, (a_c_arg_t)big_enough, Normal);
  3179.       if((*cmp_func)((QSType *)&ae_cur, (QSType *)&ae_big_enough) <= 0)
  3180.         break;
  3181.     }
  3182.     new_entry_num = big_enough - 1;
  3183.     set_moved_entryref(cur, (a_c_arg_t)big_enough);
  3184.     }
  3185.  
  3186.     (void)adrbk_get_ae(ab, cur - 1, Unlock);
  3187.     (void)adrbk_get_ae(ab, cur + 1, Unlock);
  3188.  
  3189.     return(new_entry_num);
  3190. }
  3191.  
  3192.  
  3193. /*
  3194.  * Delete an entry from the address book
  3195.  *
  3196.  * Args: ab        -- the address book
  3197.  *       entry_num -- entry to delete
  3198.  *    save_deleted -- save deleted as a #DELETED- entry
  3199.  *
  3200.  * Result: returns:  0 if all went well
  3201.  *                  -1 if there is no such entry
  3202.  *                  -2 error writing address book, check errno
  3203.  */
  3204. int
  3205. adrbk_delete(ab, entry_num, save_deleted)
  3206.     AdrBk    *ab;
  3207.     a_c_arg_t entry_num;
  3208.     int       save_deleted;
  3209. {
  3210.     int retval;
  3211.  
  3212.     if(!ab)
  3213.       return -2;
  3214.  
  3215.     (void)adrbk_get_entryref(ab, entry_num, save_deleted ? SaveDelete : Delete);
  3216.  
  3217.     retval = adrbk_write(ab, NULL, 0);
  3218.  
  3219.     if(retval == 0 && F_OFF(F_EXPANDED_DISTLISTS,ps_global))
  3220.       exp_del_nth(ab->exp, entry_num);
  3221.     
  3222.     return(retval);
  3223. }
  3224.  
  3225.  
  3226. /*
  3227.  * Delete an address out of an address list
  3228.  *
  3229.  * Args: ab    -- the address book
  3230.  *       entry -- the address list we are deleting from
  3231.  *       addr  -- address in above list to be deleted
  3232.  *
  3233.  * Result: 0: Deletion complete, address book written
  3234.  *        -1: Address for deletion not found
  3235.  *        -2: Error writing address book. Check errno.
  3236.  *
  3237.  * The address to be deleted is located by matching the string.
  3238.  *
  3239.  * This doesn't invalidate any of the AEMgr cache or anything since this
  3240.  * entry is still there when we're done and we've updated the cache
  3241.  * entry by deleting it below.
  3242.  */
  3243. int
  3244. adrbk_listdel(ab, entry_num, addr)
  3245.     AdrBk       *ab;
  3246.     a_c_arg_t   entry_num;
  3247.     char        *addr;
  3248. {
  3249.     char **p, *to_free;
  3250.     AdrBk_Entry *entry;
  3251.  
  3252.     if(!ab || entry_num >= ab->count)
  3253.       return -2;
  3254.  
  3255.     if(!addr)
  3256.       return -1;
  3257.  
  3258.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3259.  
  3260.     if(entry->tag != List){
  3261.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3262.         return -1;
  3263.     }
  3264.  
  3265.     for(p = entry->addr.list; *p; p++) 
  3266.       if(strcmp(*p, addr) == 0)
  3267.         break;
  3268.  
  3269.     if(*p == NULL)
  3270.       return -1;
  3271.  
  3272.     /* note storage to be freed */
  3273.     if(*p != empty)
  3274.       to_free = *p;
  3275.     else
  3276.       to_free = NULL;
  3277.  
  3278.     /* slide all the entries below up (including NULL) */
  3279.     for(; *p; p++)
  3280.       *p = *(p+1);
  3281.  
  3282.     if(to_free)
  3283.       fs_give((void **)&to_free);
  3284.  
  3285.     return(adrbk_write(ab, NULL, 0));
  3286. }
  3287.  
  3288.  
  3289. /*
  3290.  * Delete all addresses out of an address list
  3291.  *
  3292.  * Args: ab    -- the address book
  3293.  *       entry -- the address list we are deleting from
  3294.  *
  3295.  * Result: 0: Deletion complete, address book written
  3296.  *        -1: Address for deletion not found
  3297.  *        -2: Error writing address book. Check errno.
  3298.  *
  3299.  * This doesn't invalidate any of the AEMgr cache or anything since this
  3300.  * entry is still there when we're done and we've updated the cache
  3301.  * entry by deleting it below.
  3302.  */
  3303. int
  3304. adrbk_listdel_all(ab, entry_num)
  3305.     AdrBk       *ab;
  3306.     a_c_arg_t   entry_num;
  3307. {
  3308.     char **p;
  3309.     AdrBk_Entry *entry;
  3310.  
  3311.     if(!ab || entry_num >= ab->count)
  3312.       return -2;
  3313.  
  3314.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3315.  
  3316.     if(entry->tag != List){
  3317.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3318.         return -1;
  3319.     }
  3320.  
  3321.     /* free old list */
  3322.     for(p = entry->addr.list; p && *p; p++) 
  3323.       if(*p != empty)
  3324.         fs_give((void **)p);
  3325.     
  3326.     if(entry->addr.list)
  3327.       fs_give((void **)&(entry->addr.list));
  3328.  
  3329.     entry->addr.list = NULL;
  3330.  
  3331.     return(adrbk_write(ab, NULL, 0));
  3332. }
  3333.  
  3334.  
  3335. /*
  3336.  * Add one address to an already existing address list
  3337.  *
  3338.  * Args: ab       -- the address book
  3339.  *       entry    -- the address list we are adding to
  3340.  *       addr     -- address to be added
  3341.  *
  3342.  * Result: returns 0 : addition made, address book written
  3343.  *                -1 : addition to non-list attempted
  3344.  *                -2 : error writing address book -- check errno
  3345.  */
  3346. int
  3347. adrbk_listadd(ab, entry_num, addr)
  3348.     AdrBk       *ab;
  3349.     a_c_arg_t    entry_num;
  3350.     char        *addr;
  3351. {
  3352.     char **p;
  3353.     int    n;
  3354.     AdrBk_Entry *entry;
  3355.  
  3356.     if(!ab || entry_num >= ab->count)
  3357.       return -2;
  3358.  
  3359.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3360.  
  3361.     if(entry->tag != List){
  3362.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3363.         return -1;
  3364.     }
  3365.  
  3366.     /*--- count up size of list ------*/    
  3367.     for(p = entry->addr.list; p != NULL && *p != NULL; p++)
  3368.       ;/* do nothing */
  3369.  
  3370.     /*----- make room at end of list for it ------*/
  3371.     if(entry->addr.list == NULL){
  3372.         entry->addr.list = (char **)fs_get(2 * sizeof(char *));
  3373.         n = 1;
  3374.     }
  3375.     else{
  3376.         n = p - entry->addr.list;
  3377.         n++;
  3378.     /* n is size of list, +1 for NULL */
  3379.         fs_resize((void **)&entry->addr.list, sizeof(char *) * (n + 1));
  3380.     }
  3381.  
  3382.     /*----- Put it at the end -------*/
  3383.     (entry->addr.list)[n-1] = cpystr(addr);
  3384.     (entry->addr.list)[n]   = NULL;
  3385.  
  3386.     /*---- sort it into the correct place ------*/
  3387.     if(ab->sort_rule != AB_SORT_RULE_NONE)
  3388.       sort_addr_list(entry->addr.list);
  3389.  
  3390.     return(adrbk_write(ab, NULL, 0));
  3391. }
  3392.  
  3393.  
  3394. /*
  3395.  * Add a list of addresses to an already existing address list
  3396.  *
  3397.  * Args: ab       -- the address book
  3398.  *       entry    -- the address list we are adding to
  3399.  *       addrs    -- address list to be added
  3400.  *
  3401.  * Result: returns 0 : addition made, address book written
  3402.  *                -1 : addition to non-list attempted
  3403.  *                -2 : error writing address book -- check errno
  3404.  */
  3405. int
  3406. adrbk_nlistadd(ab, entry_num, addrs)
  3407. AdrBk       *ab;
  3408. a_c_arg_t    entry_num;
  3409. char       **addrs;
  3410. {
  3411.     char **p;
  3412.     int    cur_size, size_of_additional_list, new_size;
  3413.     int    i;
  3414.     AdrBk_Entry *entry;
  3415.  
  3416.     if(!ab || entry_num >= ab->count)
  3417.       return -2;
  3418.  
  3419.     entry = adrbk_get_ae(ab, entry_num, Lock);
  3420.  
  3421.     if(entry->tag != List){
  3422.     (void)adrbk_get_entryref(ab, entry_num, Unlock);
  3423.         return -1;
  3424.     }
  3425.  
  3426.     /* count up size of existing list */    
  3427.     for(p = entry->addr.list; p != NULL && *p != NULL; p++)
  3428.       ;/* do nothing */
  3429.  
  3430.     cur_size = p - entry->addr.list;
  3431.  
  3432.     /* count up size of new list */    
  3433.     for(p = addrs; p != NULL && *p != NULL; p++)
  3434.       ;/* do nothing */
  3435.  
  3436.     size_of_additional_list = p - addrs;
  3437.     new_size = cur_size + size_of_additional_list;
  3438.  
  3439.     /* make room at end of list for it */
  3440.     fs_resize((void **)&entry->addr.list, sizeof(char *) * (new_size + 1));
  3441.  
  3442.     /* Put new list at the end */
  3443.     for(i = cur_size; i < new_size; i++)
  3444.       (entry->addr.list)[i] = cpystr(addrs[i - cur_size]);
  3445.  
  3446.     (entry->addr.list)[new_size] = NULL;
  3447.  
  3448.     /*---- sort it into the correct place ------*/
  3449.     if(ab->sort_rule != AB_SORT_RULE_NONE)
  3450.       sort_addr_list(entry->addr.list);
  3451.  
  3452.     return(adrbk_write(ab, NULL, 0));
  3453. }
  3454.  
  3455.  
  3456. /*
  3457.  * Close address book
  3458.  *
  3459.  * All that is done here is to free the storage, since the address book is 
  3460.  * rewritten on every change.
  3461.  */
  3462. void
  3463. adrbk_close(ab)
  3464.     AdrBk *ab;
  3465. {
  3466.     if(!ab)
  3467.       return;
  3468.  
  3469.     clear_entryref_cache(ab);
  3470.     if(ab->fp)
  3471.       (void)fclose(ab->fp);
  3472.  
  3473.     if(ab->fp_hash)
  3474.       (void)fclose(ab->fp_hash);
  3475.     
  3476.     if(ab->head_cache_elem)
  3477.       fs_give((void **)&ab->head_cache_elem);
  3478.  
  3479.     if(ab->tail_cache_elem)
  3480.       fs_give((void **)&ab->tail_cache_elem);
  3481.  
  3482.     if(ab->n_ae_cached_in_this_bucket)
  3483.       fs_give((void **)&ab->n_ae_cached_in_this_bucket);
  3484.  
  3485.     if(ab->hash_by_nick)
  3486.       free_ab_adrhash(&ab->hash_by_nick);
  3487.  
  3488.     if(ab->hash_by_addr)
  3489.       free_ab_adrhash(&ab->hash_by_addr);
  3490.  
  3491.     if(ab->filename)
  3492.       fs_give((void**)&ab->filename);
  3493.  
  3494.     if(ab->orig_filename)
  3495.       fs_give((void**)&ab->orig_filename);
  3496.  
  3497.     if(ab->temp_filename)
  3498.       fs_give((void**)&ab->temp_filename);
  3499.  
  3500.     if(ab->delete_hashfile && ab->hashfile)
  3501.       unlink(ab->hashfile);
  3502.  
  3503.     if(ab->hashfile)
  3504.       fs_give((void**)&ab->hashfile);
  3505.  
  3506.     if(ab->temp_hashfile)
  3507.       fs_give((void**)&ab->temp_hashfile);
  3508.  
  3509.     if(ab->exp){
  3510.     exp_free(ab->exp);
  3511.     fs_give((void **)&ab->exp);  /* free head of list, too */
  3512.     }
  3513.  
  3514.     if(ab->checks){
  3515.     exp_free(ab->checks);
  3516.     fs_give((void **)&ab->checks);  /* free head of list, too */
  3517.     }
  3518.  
  3519.     fs_give((void **)&ab);
  3520. }
  3521.  
  3522.  
  3523. void
  3524. adrbk_partial_close(ab)
  3525.     AdrBk *ab;
  3526. {
  3527.     init_entryref_cache(ab);
  3528.     exp_free(ab->exp);  /* leaves head of list */
  3529.     exp_free(ab->checks);  /* leaves head of list */
  3530. }
  3531.  
  3532.  
  3533. void
  3534. free_ab_entryref(ab, entry)
  3535.     AdrBk *ab;
  3536.     EntryRef *entry;
  3537. {
  3538.     if(entry->ae)
  3539.       free_ae(ab, &entry->ae);
  3540.  
  3541.     fs_give((void **)&entry);
  3542. }
  3543.  
  3544.  
  3545. static adrbk_cntr_t tot_for_percent;
  3546. static adrbk_cntr_t entry_num_for_percent;
  3547. #define AB_RENAMED_ABOOK  0x1
  3548. #define AB_RENAMED_HASH   0x2
  3549.  
  3550. /*
  3551.  * Write out the address book.
  3552.  *
  3553.  * Format is as in comment in the adrbk_open routine.  Lines are wrapped
  3554.  * to be under 80 characters.  This is called on every change to the
  3555.  * address book.  Write is first to a temporary file,
  3556.  * which is then renamed to be the real address book so that we won't
  3557.  * destroy the real address book in case of something like a full file
  3558.  * system.
  3559.  *
  3560.  * Writing a temp file and then renaming has the bad side affect of
  3561.  * destroying links.  It also overrides any read only permissions on
  3562.  * the mail file since rename ignores such permissions.  However, we
  3563.  * handle readonly-ness in addrbook.c before we call this.
  3564.  * We retain the permissions by doing a stat on the old file and a
  3565.  * chmod on the new one.
  3566.  *
  3567.  * Returns:   0 write was successful
  3568.  *           -2 write failed
  3569.  *           -5 interrupted
  3570.  */
  3571. int
  3572. adrbk_write(ab, sort_array, be_quiet)
  3573.     AdrBk *ab;
  3574.     adrbk_cntr_t *sort_array;
  3575.     int be_quiet;
  3576. {
  3577.     FILE                  *ab_stream = NULL,
  3578.                           *fp_for_hash = NULL;
  3579.     EntryRef              *entry;
  3580.     AdrBk_Entry           *ae = NULL;
  3581.     adrbk_cntr_t           entry_num;
  3582.     time_t                 mtime;
  3583.     long                   saved_deleted_cnt;
  3584.     adrbk_cntr_t           hash,
  3585.                            new_htable_size;
  3586. #ifndef    DOS
  3587.     void                  (*save_sighup)();
  3588. #endif
  3589.     int                   max_nick = 0,
  3590.               max_full = 0, full_two = 0, full_three = 0,
  3591.               max_fcc = 0, fcc_two = 0, fcc_three = 0,
  3592.               max_addr = 0, addr_two = 0, addr_three = 0,
  3593.               this_nick_width, this_full_width, this_addr_width,
  3594.               this_fcc_width;
  3595.     int                   progress = 0, interrupt_happened = 0, we_cancel = 0;
  3596.     WIDTH_INFO_S         *widths;
  3597.  
  3598.     if(!ab)
  3599.       return -2;
  3600.  
  3601.     dprint(2, (debugfile, "- adrbk_write(\"%s\") - writing %lu entries\n",
  3602.     ab->filename ? ab->filename : "", (unsigned long)ab->count));
  3603.     
  3604.     errno = 0;
  3605.  
  3606.     /* verify that file has not been changed by something else */
  3607.     if(ab->last_change_we_know_about != (time_t)(-1) &&
  3608.     (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
  3609.     ab->last_change_we_know_about != mtime){
  3610.     /* It has changed! */
  3611.     q_status_message(SM_ORDER | SM_DING, 5, 15,
  3612.         "Addrbook changed by another process, aborting our change to avoid damage...");
  3613.     dprint(1,
  3614.         (debugfile,
  3615. "adrbk_write: addrbook %s changed while we had it open, aborting adrbk_write\n",
  3616.         ab->filename));
  3617.     longjmp(addrbook_changed_unexpectedly, 1);
  3618.     /*NOTREACHED*/
  3619.     }
  3620.  
  3621.     if(!be_quiet){
  3622.     tot_for_percent = ab->count;
  3623.     entry_num_for_percent = 0;
  3624.     we_cancel = busy_alarm(1, "Saving address book",percent_abook_saved,1);
  3625.     }
  3626.  
  3627. #define TABWIDTH 8
  3628. #define INDENTSTR "   "
  3629. #define INDENT 3  /* length of INDENTSTR */
  3630.  
  3631. #ifndef    DOS
  3632.     save_sighup = (void (*)())signal(SIGHUP, SIG_IGN);
  3633. #endif
  3634.  
  3635.     if(ab->temp_filename == NULL
  3636.        || (ab_stream = fopen(ab->temp_filename, "w")) == NULL){
  3637. #ifndef    DOS
  3638.         (void)signal(SIGHUP, save_sighup);
  3639. #endif
  3640.         return -2;
  3641.     }
  3642.  
  3643.     /* Rebuild on-disk hash tables from incore hash tables */
  3644.     if((fp_for_hash = fopen(ab->temp_hashfile, WRITE_MODE)) == NULL)
  3645.     dprint(2,
  3646.         (debugfile,
  3647.         "adrbk_write(%s): failed opening temp hashfile (%s)\n",
  3648.         ab->filename,
  3649.         "ab->temp_hashfile"));
  3650.  
  3651.     /* clear hash tables */
  3652.     new_htable_size = hashtable_size((a_c_arg_t)ab->count);
  3653.     if(new_htable_size == ab->htable_size){
  3654.     init_adrhash_array(ab->hash_by_nick, (a_c_arg_t)ab->htable_size);
  3655.     init_adrhash_array(ab->hash_by_addr, (a_c_arg_t)ab->htable_size);
  3656.     }
  3657.     else{
  3658.     free_ab_adrhash(&ab->hash_by_nick);
  3659.     free_ab_adrhash(&ab->hash_by_addr);
  3660.     ab->htable_size  = new_htable_size;
  3661.     ab->hash_by_nick = new_adrhash((a_c_arg_t)ab->htable_size);
  3662.     ab->hash_by_addr = new_adrhash((a_c_arg_t)ab->htable_size);
  3663.     }
  3664.  
  3665.     saved_deleted_cnt = ab->deleted_cnt;
  3666.  
  3667.     /* accept keyboard interrupts */
  3668.     intr_handling_on();
  3669.  
  3670.     if(write_hash_header(fp_for_hash, (a_c_arg_t)ab->htable_size))
  3671.       goto io_error;
  3672.  
  3673.     writing = 1;
  3674.  
  3675.     /*
  3676.      * If there was an entry that just got deleted, add it at top.
  3677.      */
  3678.     if(deleted_ae){
  3679.     struct tm *tm_now;
  3680.     time_t     now;
  3681.  
  3682.     now = time((time_t *)0);
  3683.     tm_now = localtime(&now);
  3684.     fprintf(ab_stream, "%s%02d/%02d/%02d#",
  3685.         DELETED, (tm_now->tm_year)%100, tm_now->tm_mon+1, tm_now->tm_mday);
  3686.     if(write_single_abook_entry(deleted_ae, ab_stream, &this_nick_width,
  3687.         &this_full_width, &this_addr_width, &this_fcc_width) == EOF)
  3688.       goto io_error;
  3689.     
  3690.     ab->deleted_cnt++;
  3691.     free_ae(ab, &deleted_ae);
  3692.     }
  3693.  
  3694.     /*
  3695.      * If there are any old deleted entries, copy them to new file.
  3696.      * We do so by scanning through the raw file looking for nicknames
  3697.      * which start with the deleted string.
  3698.      */
  3699.     if(saved_deleted_cnt > 0L){
  3700.     int rew = 1, c;
  3701.     FILE *fp_in;
  3702.     long offset, cnt_down, length;
  3703.     char *nickname;
  3704.  
  3705.     fp_in = ab->fp;
  3706.     if(!fp_in)
  3707.       goto io_error;
  3708.  
  3709.     cnt_down = saved_deleted_cnt;
  3710.     while(cnt_down > 0L
  3711.           && (nickname = skip_to_next_nickname(fp_in,&offset,NULL,&length,
  3712.                            rew,NULL)) != NULL){
  3713.         rew = 0;
  3714.  
  3715.         if(strncmp(nickname, DELETED, DELETED_LEN) == 0
  3716.            && isdigit((unsigned char)nickname[DELETED_LEN])
  3717.            && isdigit((unsigned char)nickname[DELETED_LEN+1])
  3718.            && nickname[DELETED_LEN+2] == '/'
  3719.            && isdigit((unsigned char)nickname[DELETED_LEN+3])
  3720.            && isdigit((unsigned char)nickname[DELETED_LEN+4])
  3721.            && nickname[DELETED_LEN+5] == '/'
  3722.            && isdigit((unsigned char)nickname[DELETED_LEN+6])
  3723.            && isdigit((unsigned char)nickname[DELETED_LEN+7])
  3724.            && nickname[DELETED_LEN+8] == '#'){
  3725.         long save_offset;
  3726.         int year, month, day;
  3727.         struct tm *tm_before;
  3728.         time_t     now, before;
  3729.  
  3730.         cnt_down--;
  3731.         now = time((time_t *)0);
  3732.         before = now - (time_t)ABOOK_DELETED_EXPIRE_TIME;
  3733.         tm_before = localtime(&before);
  3734.         tm_before->tm_mon++;
  3735.  
  3736.         /*
  3737.          * Check to see if it is older than 100 days.
  3738.          */
  3739.         year  = atoi(&nickname[DELETED_LEN]);
  3740.         month = atoi(&nickname[DELETED_LEN+3]);
  3741.         day   = atoi(&nickname[DELETED_LEN+6]);
  3742.         if(year < 95)
  3743.           year += 100;  /* this breaks in year 2095 */
  3744.         
  3745.         /*
  3746.          * only write it if it is less than 100 days old
  3747.          */
  3748.         if(year > tm_before->tm_year
  3749.            || (year == tm_before->tm_year
  3750.                && month > tm_before->tm_mon)
  3751.            || (year == tm_before->tm_year
  3752.                && month == tm_before->tm_mon
  3753.                && day >= tm_before->tm_mday)){
  3754.  
  3755.             save_offset = ftell(fp_in);
  3756.             if(fseek(fp_in, offset, 0))
  3757.               goto io_error;
  3758.  
  3759.             while(length-- > 0L)
  3760.               if((c = getc(fp_in)) == EOF || putc(c, ab_stream) == EOF)
  3761.             goto io_error;
  3762.  
  3763.             if(fseek(fp_in, save_offset, 0))
  3764.               goto io_error;
  3765.         }
  3766.         else
  3767.           ab->deleted_cnt--;
  3768.         }
  3769.     }
  3770.  
  3771.     if(cnt_down != 0L)
  3772.       dprint(2, (debugfile,
  3773.         "adrbk_write(%s): Can't find %d commented deleteds\n",
  3774.         ab->filename, cnt_down));
  3775.     }
  3776.  
  3777.     for(entry_num = 0; entry_num < ab->count; entry_num++){
  3778.  
  3779.     entry_num_for_percent++;
  3780.  
  3781.     ALARM_BLIP();
  3782.  
  3783.     if(sort_array)
  3784.       entry = adrbk_get_entryref(ab, (a_c_arg_t)(sort_array[entry_num]),
  3785.         Normal);
  3786.     else
  3787.       entry = adrbk_get_entryref(ab, (a_c_arg_t)entry_num, Normal);
  3788.  
  3789.     if(entry == NULL || entry->uid_nick == NO_UID){
  3790.         dprint(1,
  3791.         (debugfile,
  3792.         "adrbk_write(%s): premature end while writing addrbook (%s)\n",
  3793.         ab->filename,
  3794.         (entry == NULL) ? "entry is NULL" : "uid_nick is NO_UID"));
  3795.         goto io_error;
  3796.     }
  3797.  
  3798.     ae = init_ae_entry(ab, entry); /* free it ourselves below */
  3799.     if(ae == (AdrBk_Entry *)NULL){
  3800.         dprint(1,
  3801.         (debugfile,
  3802.         "adrbk_write(%s): can't find ae while writing addrbook\n",
  3803.         ab->filename));
  3804.         goto io_error;
  3805.     }
  3806.  
  3807.     /*
  3808.      * This works because the information is still correct for the
  3809.      * old file.  The offsets for the old file are still the correct
  3810.      * values, the offsets that get set below are for the temporary
  3811.      * file, which will become the new file at the end.  There may
  3812.      * be some ae's that don't have offsets set yet, but they should
  3813.      * have cached ae values set if we did everything correctly.
  3814.      */
  3815.  
  3816.     /* adjust EntryRef for this entry */
  3817.     entry->uid_nick  = ab_uid(ae->nickname);
  3818.     entry->offset    = ftell(ab_stream);
  3819.     entry->ae        = (AdrBk_Entry *)NULL;
  3820.     hash             = ab_hash(ae->nickname, (a_c_arg_t)ab->htable_size);
  3821.     entry->next_nick = ab->hash_by_nick->harray[hash];
  3822.     ab->hash_by_nick->harray[hash] = entry_num;
  3823.     if(ae->tag == Single){
  3824.         entry->uid_addr  = ab_uid_addr(ae->addr.addr);
  3825.         hash             = ab_hash_addr(ae->addr.addr,
  3826.                     (a_c_arg_t)ab->htable_size);
  3827.         entry->next_addr = ab->hash_by_addr->harray[hash];
  3828.         ab->hash_by_addr->harray[hash] = entry_num;
  3829.     }
  3830.     else{
  3831.         entry->uid_addr  = NO_UID;
  3832.         entry->next_addr = NO_NEXT;
  3833.     }
  3834.  
  3835.     /* this writes to the OnDisk hashtable */
  3836.     if(write_single_entryref(entry, fp_for_hash))
  3837.           goto io_error;
  3838.  
  3839.     /* write to temp file */
  3840.     if(write_single_abook_entry(ae, ab_stream, &this_nick_width,
  3841.         &this_full_width, &this_addr_width, &this_fcc_width) == EOF)
  3842.           goto io_error;
  3843.  
  3844.     free_ae(ab, &ae);
  3845.  
  3846.     /* keep track of widths */
  3847.     max_nick = max(max_nick, this_nick_width);
  3848.     if(this_full_width > max_full){
  3849.         full_three = full_two;
  3850.         full_two   = max_full;
  3851.         max_full   = this_full_width;
  3852.     }
  3853.     else if(this_full_width > full_two){
  3854.         full_three = full_two;
  3855.         full_two   = this_full_width;
  3856.     }
  3857.     else if(this_full_width > full_three){
  3858.         full_three = this_full_width;
  3859.     }
  3860.  
  3861.     if(this_addr_width > max_addr){
  3862.         addr_three = addr_two;
  3863.         addr_two   = max_addr;
  3864.         max_addr   = this_addr_width;
  3865.     }
  3866.     else if(this_addr_width > addr_two){
  3867.         addr_three = addr_two;
  3868.         addr_two   = this_addr_width;
  3869.     }
  3870.     else if(this_addr_width > addr_three){
  3871.         addr_three = this_addr_width;
  3872.     }
  3873.  
  3874.     if(this_fcc_width > max_fcc){
  3875.         fcc_three = fcc_two;
  3876.         fcc_two   = max_fcc;
  3877.         max_fcc   = this_fcc_width;
  3878.     }
  3879.     else if(this_fcc_width > fcc_two){
  3880.         fcc_three = fcc_two;
  3881.         fcc_two   = this_fcc_width;
  3882.     }
  3883.     else if(this_fcc_width > fcc_three){
  3884.         fcc_three = this_fcc_width;
  3885.     }
  3886.  
  3887.     /*
  3888.      * Check to see if we've been interrupted.  We check at the bottom
  3889.      * of the loop so that we can handle an interrupt in the last
  3890.      * iteration.
  3891.      */
  3892.     if(ps_global->intr_pending){
  3893.         interrupt_happened++;
  3894.         goto io_error;
  3895.     }
  3896.     }
  3897.  
  3898.     intr_handling_off();
  3899.  
  3900.     if(fclose(ab_stream) == EOF)
  3901.       goto io_error;
  3902.  
  3903.     ab_stream = (FILE *)NULL;
  3904.  
  3905.     file_attrib_copy(ab->temp_filename, ab->filename);
  3906.     if(ab->fp){
  3907.     (void)fclose(ab->fp);
  3908.     ab->fp = NULL;            /* in case of problems */
  3909.     }
  3910.  
  3911.     if(rename_file(ab->temp_filename, ab->filename) < 0)
  3912.       goto io_error;
  3913.  
  3914.     /* reopen fp to new file */
  3915.     ab->fp = fopen(ab->filename, READ_MODE);
  3916.  
  3917.     progress |= AB_RENAMED_ABOOK;
  3918.  
  3919.     widths = &ab->widths;
  3920.     widths->max_nickname_width  = max_nick;
  3921.     widths->max_fullname_width  = max_full;
  3922.     widths->max_addrfield_width = max_addr;
  3923.     widths->max_fccfield_width  = max_fcc;
  3924.     widths->third_biggest_fullname_width  = full_three;
  3925.     widths->third_biggest_addrfield_width = addr_three;
  3926.     widths->third_biggest_fccfield_width  = fcc_three;
  3927.  
  3928.     /* record new change date of addrbook file */
  3929.     ab->last_change_we_know_about = get_adj_fp_file_mtime(ab->fp);
  3930.     
  3931.     if(write_hash_table(ab->hash_by_nick, fp_for_hash,
  3932.     (a_c_arg_t)ab->htable_size))
  3933.       goto io_error;
  3934.  
  3935.     if(write_hash_table(ab->hash_by_addr, fp_for_hash,
  3936.     (a_c_arg_t)ab->htable_size))
  3937.       goto io_error;
  3938.  
  3939.     if(write_hash_trailer(ab, fp_for_hash, 1))
  3940.       goto io_error;
  3941.  
  3942.     if(fclose(fp_for_hash) == EOF)
  3943.       goto io_error;
  3944.  
  3945.     fp_for_hash = (FILE *)NULL;
  3946.  
  3947.     file_attrib_copy(ab->temp_hashfile, ab->hashfile);
  3948.     if(ab->fp_hash){
  3949.     (void)fclose(ab->fp_hash);
  3950.     ab->fp_hash = NULL;        /* in case there's a problem */
  3951.     }
  3952.  
  3953.     if(rename_file(ab->temp_hashfile, ab->hashfile) < 0)
  3954.       goto io_error;
  3955.  
  3956.     progress |= AB_RENAMED_HASH;
  3957.  
  3958.     ab->fp_hash = fopen(ab->hashfile, READ_MODE);
  3959.  
  3960. #ifndef    DOS
  3961.     (void)signal(SIGHUP, save_sighup);
  3962. #endif
  3963.  
  3964.     /* this clears the Locked state as well as making the cache correct */
  3965.     init_entryref_cache(ab);
  3966.     writing = 0;
  3967.     if(we_cancel)
  3968.       cancel_busy_alarm(0);
  3969.  
  3970.     return 0;
  3971.  
  3972.  
  3973. io_error:
  3974.     intr_handling_off();
  3975. #ifndef    DOS
  3976.     (void)signal(SIGHUP, save_sighup);
  3977. #endif
  3978.     if(interrupt_happened){
  3979.     q_status_message(0, 1, 2, "Interrupt!  Reverting to previous version");
  3980.     display_message('x');
  3981.     dprint(2,
  3982.         (debugfile, "adrbk_write(%s): Interrupt\n", ab->filename));
  3983.     }
  3984.     else
  3985.       dprint(2,
  3986.     (debugfile, "adrbk_write(%s): some sort of io_error\n", ab->filename));
  3987.  
  3988.     writing = 0;
  3989.     if(we_cancel)
  3990.       cancel_busy_alarm(-1);
  3991.  
  3992.     if(ae)
  3993.       free_ae(ab, &ae);
  3994.  
  3995.     if(ab_stream){
  3996.     fclose(ab_stream);
  3997.     ab_stream = (FILE *)NULL;
  3998.     }
  3999.  
  4000.     if(fp_for_hash){
  4001.     fclose(fp_for_hash);
  4002.     fp_for_hash = (FILE *)NULL;
  4003.     }
  4004.  
  4005.     if(ab && ab->temp_filename)
  4006.       unlink(ab->temp_filename);
  4007.  
  4008.     if(ab && ab->temp_hashfile)
  4009.       unlink(ab->temp_hashfile);
  4010.  
  4011.     ab->deleted_cnt = saved_deleted_cnt; /* is this necessary? */
  4012.     adrbk_partial_close(ab);
  4013.  
  4014.     /*
  4015.      * If both the address book and hash files have been replaced, this
  4016.      * means that the open failed.  This really shouldn't happen.  We don't
  4017.      * try to recover from it here.  Instead, we'll let the address book
  4018.      * trouble detection code catch it if need be.
  4019.      */
  4020.     if(progress & AB_RENAMED_HASH){
  4021.     dprint(2,
  4022.     (debugfile, "adrbk_write(%s): already renamed hashfile: %s\n",
  4023.     ab->filename, error_description(errno)));
  4024.     }
  4025.     /*
  4026.      * We're kind of in no man's land with this case.  The null fp_hash
  4027.      * should trigger an lu rebuild in adrbk_get_entryref.
  4028.      */
  4029.     else if(progress & AB_RENAMED_ABOOK){
  4030.     ab->fp_hash = (FILE *)NULL;
  4031.     free_ab_adrhash(&ab->hash_by_nick);
  4032.     free_ab_adrhash(&ab->hash_by_addr);
  4033.     }
  4034.     /*
  4035.      * We'd come here on an i/o error in the
  4036.      * main loop.  We revert to the previous version.
  4037.      */
  4038.     else{
  4039.     free_ab_adrhash(&ab->hash_by_nick);
  4040.     free_ab_adrhash(&ab->hash_by_addr);
  4041.     /* htable size is reset from file in bld... */
  4042.     (void)bld_hash_from_ondisk_hash(ab);
  4043.     }
  4044.  
  4045.     if(interrupt_happened){
  4046.     errno = EINTR;  /* for nicer error message */
  4047.     return -5;
  4048.     }
  4049.     else
  4050.       return -2;
  4051. }
  4052.  
  4053.  
  4054. /*
  4055.  * Writes one addrbook entry with wrapping.  Fills in widths.
  4056.  * Returns 0, or EOF on error.
  4057.  *
  4058.  * Continuation lines always start with spaces.  Tabs are treated as
  4059.  * separators, never as whitespace.  When we output tab separators we
  4060.  * always put them on the ends of lines, never on the start of a line
  4061.  * after a continuation.  That is, there is always something printable
  4062.  * after continuation spaces.
  4063.  */
  4064. int
  4065. write_single_abook_entry(ae, fp, ret_nick_width, ret_full_width,
  4066.              ret_addr_width, ret_fcc_width)
  4067.     AdrBk_Entry *ae;
  4068.     FILE        *fp;
  4069.     int         *ret_nick_width, *ret_full_width,
  4070.         *ret_addr_width, *ret_fcc_width;
  4071. {
  4072.     register int len;
  4073.     int nick_width = 0, full_width = 0, addr_width = 0, fcc_width = 0;
  4074.     int tmplen, this_width;
  4075.     char *p;
  4076.  
  4077.     /* write to temp file */
  4078.     if(fputs(ae->nickname, fp) == EOF)
  4079.       return(EOF);
  4080.  
  4081.     nick_width = strlen(ae->nickname);
  4082.     putc(TAB, fp);
  4083.     len = nick_width + (TABWIDTH - nick_width % TABWIDTH);
  4084.     if(ae->fullname){
  4085.     p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  4086.                             ae->fullname, NULL);
  4087.     full_width = strlen(p);
  4088.     if(p == ae->fullname)
  4089.       this_width = full_width;
  4090.     else
  4091.       this_width = strlen(ae->fullname);
  4092.  
  4093.     len += (this_width + (TABWIDTH - this_width % TABWIDTH));
  4094.     if(len > 80){
  4095.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4096.           return(EOF);
  4097.  
  4098.         tmplen = this_width + INDENT;
  4099.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  4100.     }
  4101.  
  4102.     if(fputs(ae->fullname, fp) == EOF)
  4103.       return(EOF);
  4104.     }
  4105.     else{
  4106.     full_width = 0;
  4107.     len += TABWIDTH;
  4108.     }
  4109.  
  4110.     putc(TAB, fp);
  4111.     /* special case, make sure empty list has () */
  4112.     if(ae->tag == List && ae->addr.list == NULL){
  4113.     addr_width = 0;
  4114.     putc('(', fp);
  4115.     putc(')', fp);
  4116.     }
  4117.     else if(ae->addr.addr != NULL || ae->addr.list != NULL){
  4118.     if(ae->tag == Single){
  4119.         /*----- Single: just one address ----*/
  4120.         p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  4121.                             ae->addr.addr, NULL);
  4122.         addr_width = strlen(p);
  4123.         if(p == ae->addr.addr)
  4124.           this_width = addr_width;
  4125.         else
  4126.           this_width = strlen(ae->addr.addr);
  4127.  
  4128.         len += (this_width + (TABWIDTH - this_width % TABWIDTH));
  4129.         if(len > 80){
  4130.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4131.           return(EOF);
  4132.  
  4133.         tmplen = this_width + INDENT;
  4134.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  4135.         }
  4136.  
  4137.         if(fputs(ae->addr.addr, fp) == EOF)
  4138.           return(EOF);
  4139.     }
  4140.     else if(ae->tag == List){
  4141.         register char **a2;
  4142.  
  4143.         /*----- List: a distribution list ------*/
  4144.         putc('(', fp);
  4145.         len++;
  4146.         addr_width = 0;
  4147.         for(a2 = ae->addr.list; *a2 != NULL; a2++){
  4148.         if(a2 != ae->addr.list){
  4149.             putc(',', fp);
  4150.             len++;
  4151.         }
  4152.  
  4153.         /*
  4154.          * comma or ) also follows, so we're breaking at
  4155.          * no more than 79 chars
  4156.          */
  4157.         p = (char *)rfc1522_decode((unsigned char *)tmp_20k_buf,
  4158.                         *a2, NULL);
  4159.         tmplen = strlen(p);
  4160.         if(p == *a2)
  4161.           this_width = tmplen;
  4162.         else
  4163.           this_width = strlen(*a2);
  4164.  
  4165.         addr_width = max(addr_width, tmplen);
  4166.         if(len + this_width > 78 && len != INDENT){
  4167.             /*--- break up long lines ----*/
  4168.             if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4169.               return(EOF);
  4170.  
  4171.             len = INDENT;
  4172.         }
  4173.  
  4174.         if(fputs(*a2, fp) == EOF)
  4175.           return(EOF);
  4176.  
  4177.         len += this_width;
  4178.         }
  4179.  
  4180.         putc(')', fp);
  4181.     }
  4182.     }
  4183.  
  4184.     /* If either fcc or extra exists, output both, otherwise, neither */
  4185.     if((ae->fcc && ae->fcc[0]) || (ae->extra && ae->extra[0])){
  4186.     putc(TAB, fp);
  4187.     len += (TABWIDTH - len % TABWIDTH);
  4188.     if(ae->fcc && ae->fcc[0]){
  4189.         fcc_width = strlen(ae->fcc);
  4190.         len += (fcc_width + (TABWIDTH - fcc_width%TABWIDTH));
  4191.         if(len > 80){
  4192.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4193.           return(EOF);
  4194.  
  4195.         tmplen = fcc_width + INDENT;
  4196.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  4197.         }
  4198.  
  4199.         if(fputs(ae->fcc, fp) == EOF)
  4200.           return(EOF);
  4201.     }
  4202.     else
  4203.       fcc_width = 0;
  4204.  
  4205.     putc(TAB, fp);
  4206.     if(ae->extra && ae->extra[0]){
  4207.         tmplen = strlen(ae->extra);
  4208.         len += (tmplen + (TABWIDTH - tmplen % TABWIDTH));
  4209.         if(len > 80){
  4210.         if(fprintf(fp, "\n%s", INDENTSTR) == EOF)
  4211.           return(EOF);
  4212.  
  4213.         tmplen += INDENT;
  4214.         len = tmplen + (TABWIDTH - tmplen % TABWIDTH);
  4215.         }
  4216.  
  4217.         if(fputs(ae->extra, fp) == EOF)
  4218.           return(EOF);
  4219.     }
  4220.     }
  4221.     else
  4222.       fcc_width = 0;
  4223.  
  4224.     putc('\n', fp);
  4225.     
  4226.     if(ret_nick_width)
  4227.       *ret_nick_width = nick_width;
  4228.     if(ret_full_width)
  4229.       *ret_full_width = full_width;
  4230.     if(ret_addr_width)
  4231.       *ret_addr_width = addr_width;
  4232.     if(ret_fcc_width)
  4233.       *ret_fcc_width = fcc_width;
  4234.  
  4235.     return(0);
  4236. }
  4237.  
  4238.  
  4239. int
  4240. percent_abook_saved()
  4241. {
  4242.     return((int)(((unsigned long)entry_num_for_percent * (unsigned long)100) /
  4243.     (unsigned long)tot_for_percent));
  4244. }
  4245.  
  4246.  
  4247. /*
  4248.  * Free memory associated with entry ae.
  4249.  *
  4250.  * Args:  ab  -- Address book
  4251.  *        ae  -- Address book entry to be freed.
  4252.  */
  4253. void
  4254. free_ae(ab, ae)
  4255.     AdrBk        *ab;
  4256.     AdrBk_Entry **ae;
  4257. {
  4258.     char **p;
  4259.  
  4260.     if(!ab || !ae || !(*ae))
  4261.       return;
  4262.  
  4263.     if((*ae)->nickname && (*ae)->nickname != empty)
  4264.       fs_give((void **)&(*ae)->nickname);
  4265.  
  4266.     if((*ae)->fullname && (*ae)->fullname != empty)
  4267.       fs_give((void **)&(*ae)->fullname);
  4268.  
  4269.     if((*ae)->tag == Single){
  4270.         if((*ae)->addr.addr && (*ae)->addr.addr != empty)
  4271.           fs_give((void **)&(*ae)->addr.addr);
  4272.     }
  4273.     else if((*ae)->tag == List){
  4274.         if((*ae)->addr.list){
  4275.             for(p = (*ae)->addr.list; *p; p++) 
  4276.               if(*p != empty)
  4277.             fs_give((void **)p);
  4278.  
  4279.             fs_give((void **)&(*ae)->addr.list);
  4280.         }
  4281.     }
  4282.  
  4283.     if((*ae)->fcc && (*ae)->fcc != empty)
  4284.       fs_give((void **)&(*ae)->fcc);
  4285.  
  4286.     if((*ae)->extra && (*ae)->extra != empty)
  4287.       fs_give((void **)&(*ae)->extra);
  4288.  
  4289.     fs_give((void **)ae);
  4290. }
  4291.  
  4292.  
  4293. /*
  4294.  * Writes the Header part of on-disk hash table.
  4295.  * Returns 0 on success, -1 on failure.
  4296.  */
  4297. int
  4298. write_hash_header(fp, size)
  4299.     FILE        *fp;
  4300.     a_c_arg_t    size;
  4301. {
  4302.     dprint(9, (debugfile, "- write_hash_header -\n"));
  4303.  
  4304.     if(fp == (FILE *)NULL)
  4305.       return -1;
  4306.  
  4307.     /* 10 is SIZEOF_HASH_SIZE */
  4308. #ifdef HUGE_ADDRBOOKS
  4309.     if(fprintf(fp, "%s %s %10lu\n", PMAGIC, ADRHASH_FILE_VERSION_NUM,
  4310.     (unsigned long)size) == EOF)
  4311. #else /* !HUGE_ADDRBOOKS */
  4312.     if(fprintf(fp, "%s %s %5u\n", PMAGIC, ADRHASH_FILE_VERSION_NUM,
  4313.     (unsigned)size) == EOF)
  4314. #endif /* !HUGE_ADDRBOOKS */
  4315.     return -1;
  4316.     
  4317.     return 0;
  4318. }
  4319.  
  4320.  
  4321. int
  4322. write_single_entryref(entry, fp)
  4323.     EntryRef *entry;
  4324.     FILE    *fp;
  4325. {
  4326.     if(fp == (FILE *)NULL)
  4327.       return -1;
  4328.  
  4329.     if(entry == (EntryRef *)NULL || entry->uid_nick == NO_UID)
  4330.       return -1;
  4331.  
  4332.     /*
  4333.      * The first two widths below should be the same as SIZEOF_HASH_INDEX.
  4334.      * The next two the same as SIZEOF_UID.
  4335.      * The last one the same as SIZEOF_FILEOFFSET.
  4336.      */
  4337. #ifdef HUGE_ADDRBOOKS
  4338.     if(fprintf(fp, "%10lu %10lu %11ld %11ld %10ld\n",
  4339.         (unsigned long)entry->next_nick,
  4340.         (unsigned long)entry->next_addr,
  4341.         (long)entry->uid_nick,
  4342.         (long)entry->uid_addr,
  4343.         (long)entry->offset) == EOF)
  4344. #else /* !HUGE_ADDRBOOKS */
  4345.     if(fprintf(fp, "%5u %5u %11ld %11ld %10ld\n",
  4346.         (unsigned)entry->next_nick,
  4347.         (unsigned)entry->next_addr,
  4348.         (long)entry->uid_nick,
  4349.         (long)entry->uid_addr,
  4350.         (long)entry->offset) == EOF)
  4351. #endif /* !HUGE_ADDRBOOKS */
  4352.     return -1;
  4353.  
  4354.     return 0;
  4355. }
  4356.  
  4357.  
  4358. /*
  4359.  * Writes the HashTable part of on-disk hash table.
  4360.  * Returns 0 on success, -1 on failure.
  4361.  */
  4362. int
  4363. write_hash_table(ahash, fp, size_arg)
  4364.     AdrHash *ahash;
  4365.     FILE    *fp;
  4366.     a_c_arg_t size_arg;
  4367. {
  4368.     adrbk_cntr_t i;
  4369.     adrbk_cntr_t size;
  4370.  
  4371.     dprint(9, (debugfile, "- write_hash_table -\n"));
  4372.  
  4373.     size = (adrbk_cntr_t)size_arg;
  4374.  
  4375.     if(fp == (FILE *)NULL)
  4376.       return -1;
  4377.  
  4378.     /*
  4379.      * The width below should be the same as SIZEOF_HASH_INDEX.
  4380.      */
  4381.     for(i = 0; i < size; i++){
  4382. #ifdef HUGE_ADDRBOOKS
  4383.     if(fprintf(fp, "%10lu\n", (unsigned long)(ahash->harray[i])) == EOF)
  4384. #else /* !HUGE_ADDRBOOKS */
  4385.     if(fprintf(fp, "%5u\n", (unsigned)(ahash->harray[i])) == EOF)
  4386. #endif /* !HUGE_ADDRBOOKS */
  4387.         return -1;
  4388.     }
  4389.  
  4390.     return 0;
  4391. }
  4392.  
  4393.  
  4394. /*
  4395.  * Writes the Trailer part of on-disk hash table.
  4396.  * Returns 0 on success, -1 on failure.
  4397.  */
  4398. int
  4399. write_hash_trailer(ab, fp, know_its_sorted)
  4400.     AdrBk *ab;
  4401.     FILE  *fp;
  4402.     int    know_its_sorted;
  4403. {
  4404.     WIDTH_INFO_S *widths;
  4405.  
  4406.     dprint(9, (debugfile, "- write_hash_trailer -\n"));
  4407.  
  4408.     if(fp == (FILE *)NULL)
  4409.       return -1;
  4410.  
  4411.     /*
  4412.      * The width below should be the same as SIZEOF_COUNT.
  4413.      */
  4414. #ifdef HUGE_ADDRBOOKS
  4415.     if(fprintf(fp, "%s %10lu\n", PMAGIC, (unsigned long)(ab->count)) == EOF)
  4416. #else /* !HUGE_ADDRBOOKS */
  4417.     if(fprintf(fp, "%s %5u\n", PMAGIC, (unsigned)(ab->count)) == EOF)
  4418. #endif /* !HUGE_ADDRBOOKS */
  4419.       return -1;
  4420.     
  4421.     if(fprintf(fp, "%11ld\n", ab->deleted_cnt) == EOF)
  4422.       return -1;
  4423.  
  4424.     widths = &ab->widths;
  4425.     /*
  4426.      * The 2's are the same as SIZEOF_WIDTH.
  4427.      */
  4428.     if(fprintf(fp, " %2d %2d %2d %2d %2d %2d %2d\n",
  4429.         widths->max_nickname_width,
  4430.         widths->max_fullname_width,
  4431.         widths->max_addrfield_width,
  4432.         widths->max_fccfield_width,
  4433.         widths->third_biggest_fullname_width,
  4434.         widths->third_biggest_addrfield_width,
  4435.         widths->third_biggest_fccfield_width) == EOF)
  4436.     return -1;
  4437.  
  4438. #define SLOP 3  /* add to make sure date is later than addrbook file date */
  4439.     /*
  4440.      * 10 is SIZEOF_TIMESTAMP.
  4441.      * 2 is SIZEOF_SORT_RULE.
  4442.      */
  4443.     if(fprintf(fp, "%10lu %2d\n", (unsigned long)get_adj_time() + SLOP,
  4444.     know_its_sorted ? ab->sort_rule : -1) == EOF)
  4445.     return -1;
  4446.     
  4447.     return 0;
  4448. }
  4449.  
  4450.  
  4451. /*
  4452.  * Use this size hashtables for count addrbook entries.
  4453.  */
  4454. adrbk_cntr_t
  4455. hashtable_size(count)
  4456.     a_c_arg_t count;
  4457. {
  4458.     long size;
  4459.  
  4460.     /*
  4461.      * These are picked almost totally out of the blue.  Higher values cause
  4462.      * larger hashtables so longer time to write and seek through them on
  4463.      * disk (and more diskspace).  Higher also implies fewer entries per
  4464.      * hash bucket so fewer seeks through the file.  Higher is probably
  4465.      * better if we can afford it.
  4466.      */
  4467.     if(count < 100)
  4468.       size = 100L;
  4469.     else if(count < 300)
  4470.       size = 300L;
  4471.     else if(count < 600)
  4472.       size = 600L;
  4473.     else if(count < 1000)
  4474.       size = 1000L;
  4475.     else if(count < 4000)
  4476.       size = 2000L;
  4477.     else if(count < 10000)
  4478.       size = 5000L;
  4479.     else if(count < 20000)
  4480.       size = 10000L;
  4481.     else if(count < 40000)
  4482.       size = 20000L;
  4483.     else if(count < 80000)
  4484.       size = 40000L;
  4485.     else if(count < 150000)
  4486.       size = 70000L;
  4487.     else if(count < 300000)
  4488.       size = 120000L;
  4489.     else
  4490.       size = 150000L;
  4491.     
  4492.     if(size > MAX_HASHTABLE_SIZE)
  4493.       size = MAX_HASHTABLE_SIZE;
  4494.  
  4495.     return((adrbk_cntr_t)size);
  4496. }
  4497.  
  4498.  
  4499. /*
  4500.  * This manages a cache of entryrefs and their corresponding addrbook
  4501.  * entries.  The addrbook entries could be split off to either be cached
  4502.  * or not separately, but we aren't doing that now.  It is possible for an
  4503.  * entryref to be cached without its addrbook entry being instantiated, but
  4504.  * if the cached entryref does have an addrbook entry to go with it, then
  4505.  * that cached entry is freed when the entryref is removed from the cache.
  4506.  * The virtual entryref array has ab->count entries, so may be too big to keep
  4507.  * in memory.  The current caching is LRU, except for the locked entries.
  4508.  * If the referenced flag is set in the
  4509.  * associated addrbook entry, then we leave the entry in the cache.  That's
  4510.  * because the referenced flag is not stored when it is removed from the
  4511.  * cache.  If the handling==Lock, we also leave that entry in the cache.
  4512.  *
  4513.  * Handling: Normal - Will be cached by the cache mgr (this function) and may
  4514.  *                    disappear when subsequent calls to the mgr are made.
  4515.  *           Delete - Will be removed from cache if there, and will be marked
  4516.  *                    as deleted so adrbk_write will skip it.
  4517.  *       SaveDelete - Will be removed from cache if there, and will be marked
  4518.  *                    as deleted so adrbk_write will skip it.  Will also be
  4519.  *                    saved in file as #DELETED- entry.
  4520.  *            Lock  - Tells the mgr to lock it in cache until told it is ok
  4521.  *                    to release from cache.  A call with handling set to
  4522.  *                    Unlock tells the mgr it is ok to release.  Actually, a
  4523.  *                    lock_ref_count is kept so that nested locks will work
  4524.  *                    properly.  Each call to Lock increments the ref count
  4525.  *                    by one and each call to Unlock decrements.  The Unlock
  4526.  *                    only changes handling to Normal when ref count reaches 0.
  4527.  *           Unlock - See Lock description.
  4528.  */
  4529. EntryRef *
  4530. adrbk_get_entryref(ab, elem_arg, handling)
  4531.     AdrBk       *ab;
  4532.     a_c_arg_t    elem_arg;
  4533.     Handling     handling;
  4534. {
  4535.     adrbk_cntr_t elem;
  4536.     EntryRef *return_entry = (EntryRef *)NULL;
  4537.     EntryRef *new_e = (EntryRef *)NULL;
  4538.     char *p;
  4539.     char line[MAXLINE+1];
  4540.     register ER_CACHE_ELEM_S *cptr;
  4541.     adrbk_cntr_t hash_bucket;
  4542.     ER_CACHE_ELEM_S *new_cache_element, *old_first_entry;
  4543.     ER_CACHE_ELEM_S *deleted;
  4544.     ER_CACHE_ELEM_S *head, *tail;
  4545.     ER_CACHE_ELEM_S *preceding_elem, *following_elem, *previous_top_dog;
  4546.     AdrBk_Entry *ae;
  4547.     int entryref_returned_null = 0;
  4548.  
  4549.     elem = (adrbk_cntr_t)elem_arg;
  4550.  
  4551.     dprint(9, (debugfile,
  4552.     "adrbk_get_entryref(%s) - elem=%lu (%s)\n",
  4553.     ab->filename,
  4554.     (unsigned long)elem,
  4555.     handling==Normal ? "Normal" :
  4556.      handling==Delete ? "Delete" :
  4557.       handling==SaveDelete ? "SaveDelete" :
  4558.        handling==Lock ? "Lock" :
  4559.         handling==Unlock ? "Unlock" :
  4560.                 "Unknown"));
  4561.  
  4562.     if(!ab || !ab->fp_hash || elem >= ab->count)
  4563.       goto big_trouble;
  4564.     
  4565.     /*
  4566.      * Deleted_elem just marks that element as deleted, but it still shows
  4567.      * up there.  So, we have to skip over it.  The caller won't know about
  4568.      * deleted_elem, so the caller will be using element numbers as if
  4569.      * deleted wasn't there.  Adjust for that.  This is only used for
  4570.      * short times before we rewrite the adrbk to disk with adrbk_write.
  4571.      * It gives us a way to delete an entry.
  4572.      *  0  1  D  3  4  5  Looks like this to us, deleted_elem = 2.
  4573.      *  0  1     2  3  4  Treat like this.  Note, count = 5, not 6.
  4574.      *
  4575.      * insert_before is like deleted_elem, but the opposite.  If insert_before
  4576.      * is 5, that means it logically belongs before elem number 5.  Have to
  4577.      * adjust element numbers to reflect that.
  4578.      *  0  1  2  3  4  5
  4579.      *             ^
  4580.      *             insert_before = 4.
  4581.      *  0  1  2  3  5  6
  4582.      *             ^
  4583.      *             inserted_entryref goes here when you ask for 4.
  4584.      *
  4585.      * Can't have both but there is also moved_elem to move a single element
  4586.      * from one place to another.
  4587.      *  0  1  2  3  4  5  6
  4588.      *       ^         ^  moved_elem = 5, move_elem_before = 2
  4589.      *  0  1  5  2  3  4  6
  4590.      *
  4591.      *       or
  4592.      *
  4593.      *  0  1  2  3  4  5  6
  4594.      *        ^       ^   moved_elem = 2, move_elem_before = 5
  4595.      *  0  1  3  4  2  5  6
  4596.      *
  4597.      *       or          special case, move_elem_before = count
  4598.      *
  4599.      *  0  1  2  3  4  5  6
  4600.      *        ^             ^  moved_elem = 2, move_elem_before = 7
  4601.      *  0  1  3  4  5  6  2
  4602.      *
  4603.      * Can only use one of these mechanisms at a time.  The first "if" is
  4604.      * a test that at most one is being used.
  4605.      */
  4606.     if((((deleted_elem == NO_NEXT) ? 1 : 0) +
  4607.        ((moved_elem    == NO_NEXT) ? 1 : 0) +
  4608.        ((insert_before == NO_NEXT) ? 1 : 0)) < 2){
  4609.     panic("Programming botch in adrbk_get_entryref()");
  4610.     }
  4611.     else if(deleted_elem != NO_NEXT){
  4612.     if(elem >= deleted_elem)
  4613.       elem++;
  4614.     }
  4615.     else if(insert_before != NO_NEXT){
  4616.     if(elem == insert_before)
  4617.       return(&inserted_entryref);
  4618.  
  4619.     if(elem > insert_before)
  4620.       elem--;
  4621.     }
  4622.     else if(moved_elem != NO_NEXT){
  4623.     adrbk_cntr_t el_small, el_large;
  4624.  
  4625.     el_small = min(moved_elem, move_elem_before);
  4626.     el_large = max(moved_elem, move_elem_before);
  4627.  
  4628.     if(elem < el_small ||
  4629.        elem > el_large ||
  4630.        moved_elem == move_elem_before ||
  4631.        moved_elem == move_elem_before - 1){
  4632.         /* no change */
  4633.         }
  4634.     else if(moved_elem > move_elem_before){
  4635.         if(elem == move_elem_before)
  4636.           elem = moved_elem;
  4637.         else
  4638.           elem--;
  4639.     }
  4640.     else{ /* moved_elem < move_elem_before */
  4641.         if(elem == move_elem_before - 1)
  4642.           elem = moved_elem;
  4643.         else if(elem < move_elem_before)
  4644.           elem++;
  4645.     }
  4646.     }
  4647.  
  4648.  
  4649.     if(handling == Delete || handling == SaveDelete){
  4650.     /*
  4651.      * Before we continue, we first want to get the deleted entry
  4652.      * and store it for use in adrbk_write.
  4653.      */
  4654.     if(handling == SaveDelete){
  4655.         ae = adrbk_get_ae(ab, elem_arg, Normal);
  4656.         if(ae)
  4657.           deleted_ae = copy_ae(ae);
  4658.     }
  4659.  
  4660.     dprint(2, (debugfile,
  4661.        "adrbk_get_entryref: entry marked deleted, count was %lu, now %lu\n",
  4662.        (unsigned long)ab->count, (unsigned long)(ab->count - 1)));
  4663.     ab->count--;
  4664.     deleted_elem = elem;
  4665.     }
  4666.  
  4667.     hash_bucket = elem % ab->er_hashsize;
  4668.  
  4669.     head = ab->head_cache_elem[hash_bucket];
  4670.     tail = ab->tail_cache_elem[hash_bucket];
  4671.  
  4672.     /* Look for element number elem in cache */
  4673.     cptr = head;
  4674.     while(cptr && cptr->elem != elem)
  4675.       cptr = cptr->next;
  4676.  
  4677.     if(cptr){  /* it was in cache */
  4678.  
  4679.     if(handling == Delete || handling == SaveDelete){
  4680.         dprint(9, (debugfile, "deleting %u from cache\n", elem));
  4681.         deleted              = cptr;
  4682.         preceding_elem       = deleted->prev;
  4683.         following_elem       = deleted->next;
  4684.         if(preceding_elem)
  4685.           preceding_elem->next = following_elem;
  4686.         else{
  4687.         ab->head_cache_elem[hash_bucket] = following_elem;
  4688.         head = ab->head_cache_elem[hash_bucket];
  4689.         }
  4690.  
  4691.         if(following_elem)
  4692.           following_elem->prev = preceding_elem;
  4693.         else{
  4694.         ab->tail_cache_elem[hash_bucket] = preceding_elem;
  4695.         tail = ab->tail_cache_elem[hash_bucket];
  4696.         }
  4697.  
  4698.         free_ab_entryref(ab, deleted->entry);
  4699.         ab->n_ae_cached_in_this_bucket[hash_bucket]--;
  4700.         fs_give((void **)&deleted);
  4701.         return((EntryRef *)NULL);
  4702.     }
  4703.     else{
  4704.         if(handling == Unlock){
  4705.         cptr->lock_ref_count--;
  4706.         if(cptr->lock_ref_count <= 0)
  4707.           cptr->handling = Normal;
  4708.         }
  4709.         else if(handling == Lock){
  4710.         cptr->handling = Lock;
  4711.         cptr->lock_ref_count++;
  4712.         }
  4713.  
  4714.         return_entry = cptr->entry;
  4715.     }
  4716.  
  4717.     /*
  4718.      * Put this entry back at head of cache so that we remain LRU (within
  4719.      * each hash bucket).
  4720.      */
  4721.      if(cptr->prev != (ER_CACHE_ELEM_S *)NULL){
  4722.         preceding_elem         = cptr->prev;
  4723.         following_elem         = cptr->next;
  4724.         previous_top_dog       = head;
  4725.  
  4726.         preceding_elem->next   = following_elem;
  4727.         if(following_elem)
  4728.           following_elem->prev = preceding_elem;
  4729.         else
  4730.           ab->tail_cache_elem[hash_bucket] = preceding_elem;
  4731.  
  4732.         ab->head_cache_elem[hash_bucket] = cptr;
  4733.         previous_top_dog->prev = cptr;
  4734.         cptr->next             = previous_top_dog;
  4735.         cptr->prev             = (ER_CACHE_ELEM_S *)NULL;
  4736.      }
  4737.     }
  4738.     else{  /* it was not in cache */
  4739.       if(handling != Delete && handling != SaveDelete){
  4740.     p = get_entryref_line_from_disk(ab->fp_hash, line, (a_c_arg_t)elem);
  4741.  
  4742.     if(p){
  4743.  
  4744.         /* fill in entryref from disk */
  4745.  
  4746.         new_e = new_entryref(NO_UID, NO_UID, -1L);
  4747.         new_e->next_nick = (adrbk_cntr_t)strtoul(p, (char **)NULL, 10);
  4748.         p += (SIZEOF_HASH_INDEX + SIZEOF_SPACE);
  4749.         new_e->next_addr = (adrbk_cntr_t)strtoul(p, (char **)NULL, 10);
  4750.         p += (SIZEOF_HASH_INDEX + SIZEOF_SPACE);
  4751.         new_e->uid_nick  = (adrbk_uid_t)atol(p);
  4752.         p += (SIZEOF_UID + SIZEOF_SPACE);
  4753.         new_e->uid_addr  = (adrbk_uid_t)atol(p);
  4754.         p += (SIZEOF_UID + SIZEOF_SPACE);
  4755.         new_e->offset    = atol(p);
  4756.         new_e->ae        = (AdrBk_Entry *)NULL;
  4757.  
  4758.         new_cache_element = (ER_CACHE_ELEM_S *)NULL;
  4759.  
  4760.         /* Remove some old cache entries */
  4761.         if(ab->n_ae_cached_in_this_bucket[hash_bucket] >=
  4762.         CACHE_PER_ER_BUCKET){
  4763.  
  4764.         /*
  4765.          * Only delete if hasn't been referenced, for loop detect.
  4766.          * This is because the reference count will go away if we
  4767.          * delete it from the cache.
  4768.          *
  4769.          * Don't delete Locked entries.
  4770.          *
  4771.          * Find entries we can delete until we get down to the
  4772.          * number we want in cache.
  4773.          */
  4774.         for(cptr = tail; cptr; cptr = cptr->prev){
  4775.  
  4776.             if(cptr->handling == Lock)
  4777.               continue;
  4778.  
  4779.             if(cptr->entry && cptr->entry->ae){
  4780.             ae = cptr->entry->ae;
  4781.             if(ae->referenced != 0)
  4782.                 continue;
  4783.             }
  4784.  
  4785.             deleted              = cptr;
  4786.             preceding_elem       = deleted->prev;
  4787.             following_elem       = deleted->next;
  4788.             if(preceding_elem)
  4789.               preceding_elem->next = following_elem;
  4790.             else{
  4791.             ab->head_cache_elem[hash_bucket] = following_elem;
  4792.             head = ab->head_cache_elem[hash_bucket];
  4793.             }
  4794.  
  4795.             if(following_elem)
  4796.               following_elem->prev = preceding_elem;
  4797.             else{
  4798.             ab->tail_cache_elem[hash_bucket] = preceding_elem;
  4799.             tail = ab->tail_cache_elem[hash_bucket];
  4800.             }
  4801.  
  4802.             free_ab_entryref(ab, deleted->entry);
  4803.             ab->n_ae_cached_in_this_bucket[hash_bucket]--;
  4804.             if(ab->n_ae_cached_in_this_bucket[hash_bucket] >=
  4805.             CACHE_PER_ER_BUCKET){
  4806.             fs_give((void **)&deleted);
  4807.             }
  4808.             else{
  4809.             new_cache_element = deleted;
  4810.             break;
  4811.             }
  4812.         }
  4813.         }
  4814.  
  4815.         if(new_cache_element == (ER_CACHE_ELEM_S *)NULL)
  4816.         new_cache_element =
  4817.             (ER_CACHE_ELEM_S *)fs_get(sizeof(ER_CACHE_ELEM_S));
  4818.  
  4819.         /* insert new entry at head of cache */
  4820.  
  4821.         /*
  4822.          * Unlock happening here would be a mistake, because Unlock should
  4823.          * only be called after a Lock, and a Locked entry would have
  4824.          * been in the cache.  But no harm in making it act just like
  4825.          * a Normal.
  4826.          */
  4827.         new_cache_element->lock_ref_count = 0;
  4828.         if(handling == Normal || handling == Unlock)
  4829.           new_cache_element->handling = Normal;
  4830.         else if(handling == Lock){
  4831.         new_cache_element->handling = Lock;
  4832.         new_cache_element->lock_ref_count++;
  4833.         }
  4834.  
  4835.         new_cache_element->elem     = elem;
  4836.         new_cache_element->entry    = new_e;
  4837.         old_first_entry             = head;
  4838.         ab->head_cache_elem[hash_bucket] = new_cache_element;
  4839.         if(old_first_entry)
  4840.           old_first_entry->prev       = new_cache_element;
  4841.         else
  4842.           ab->tail_cache_elem[hash_bucket] = new_cache_element;
  4843.  
  4844.         new_cache_element->next     = old_first_entry;
  4845.         new_cache_element->prev     = (ER_CACHE_ELEM_S *)NULL;
  4846.         ab->n_ae_cached_in_this_bucket[hash_bucket]++;
  4847.  
  4848.         return_entry = new_e;
  4849.     }
  4850.     else{
  4851.         /*
  4852.          * This shouldn't happen.  It is possible that it might happen
  4853.          * if we get a stale NFS handle, in which case we want to reset
  4854.          * the addrbook and retry the open.
  4855.          */
  4856.         entryref_returned_null = 1;
  4857.     }
  4858.       }
  4859.     }
  4860.  
  4861. big_trouble:
  4862.     if(return_entry == (EntryRef *)NULL){
  4863.     if(!ab){
  4864.         dprint(1, (debugfile,
  4865.           "adrbk_get_entryref: ab is NULL, should not happen.\n"));
  4866.     }
  4867.     else if(!ab->fp_hash){
  4868.         dprint(1, (debugfile,
  4869.           "adrbk_get_entryref: ab->fp_hash is NULL, should not happen.\n"));
  4870.     }
  4871.     else if(elem >= ab->count){
  4872.       dprint(1, (debugfile,
  4873.      "adrbk_get_entryref: elem >= ab->count (%ld >= %ld), should not happen.\n",
  4874.      (long)elem, (long)ab->count));
  4875.     }
  4876.     else if(entryref_returned_null){
  4877.         dprint(1, (debugfile,
  4878.       "\n\n ADDR    ::: the addressbook lookup file %s\n",
  4879.           ab->hashfile ? ab->hashfile : "?"));
  4880.         dprint(1, (debugfile,
  4881.       " BOOK    ::: seems to be unreadable.  The lookup\n"));
  4882.         dprint(1, (debugfile,
  4883.       " TROUBLE ::: file may have to be removed and rebuilt.\n"));
  4884.         dprint(1, (debugfile,
  4885.      "         ::: Usually it will fix itself, but if it doesn't, or if it\n"));
  4886.         dprint(1, (debugfile,
  4887.       "         ::: is building temporary lookup files for each user,\n"));
  4888.         dprint(1, (debugfile,
  4889.       "         ::: the sys admin should rebuild it (%s).\n\n",
  4890.           ab->hashfile ? ab->hashfile : "?"));
  4891.     }
  4892.     else{
  4893.         dprint(1, (debugfile,
  4894.           "adrbk_get_entryref: returned NULL\n"));
  4895.     }
  4896.  
  4897.     dprint(1, (debugfile,
  4898.       "There must have been a problem opening or closing or something.\n"));
  4899.  
  4900.     q_status_message1(SM_ORDER | SM_DING, 5, 5, "Addrbook problems%s",
  4901.               (trouble_rebuilds<MAX_TROUBLE_REBUILDS)
  4902.                 ? ", will attempt to resync..." : "");
  4903.  
  4904.     if(trouble_rebuilds < MAX_TROUBLE_REBUILDS){
  4905.         dprint(1, (debugfile,
  4906.           "Will attempt to longjmp to safe place and try again.\n"));
  4907.  
  4908.         if(writing){
  4909.            writing = 0;
  4910.            q_status_message(SM_ORDER, 3, 5,
  4911.            "Aborting our change to avoid damage...");
  4912.         }
  4913.  
  4914.         trouble_rebuilds++;
  4915.         /* jump back to a safe place */
  4916.         trouble_filename = (ab && ab->orig_filename)
  4917.                 ? cpystr(ab->orig_filename)
  4918.                 : cpystr("");
  4919.         longjmp(addrbook_changed_unexpectedly, 1);
  4920.         /*NOTREACHED*/
  4921.     }
  4922.     }
  4923.  
  4924.     return(return_entry);
  4925. }
  4926.  
  4927.  
  4928. /*
  4929.  * It is safe to set this higher than the number of entries in the
  4930.  * addrbook, in the sense that it will only use the number of entries.  It
  4931.  * will, however, use up lots of memory if that number is big.
  4932.  */
  4933. long
  4934. adrbk_set_nominal_cachesize(ab, new_size)
  4935.     AdrBk *ab;
  4936.     long   new_size;
  4937. {
  4938.     long old_size;
  4939.  
  4940.     old_size = ab->nominal_max_cached;
  4941.  
  4942.     dprint(9, (debugfile, "- adrbk_set_nominal_cachesize - was %ld now %ld\n",
  4943.     old_size, new_size));
  4944.  
  4945.     ab->nominal_max_cached = new_size;
  4946.  
  4947.     init_entryref_cache(ab);
  4948.  
  4949.     return(old_size);
  4950. }
  4951.  
  4952.  
  4953. long
  4954. adrbk_get_nominal_cachesize(ab)
  4955.     AdrBk *ab;
  4956. {
  4957.     return(ab->nominal_max_cached);
  4958. }
  4959.  
  4960.  
  4961. /*
  4962.  * Adds a new entryref which points to new_ae before put_it_before_this.
  4963.  */
  4964. void
  4965. set_inserted_entryref(ab, put_it_before_this, new_ae)
  4966.     AdrBk *ab;
  4967.     a_c_arg_t put_it_before_this;
  4968.     AdrBk_Entry *new_ae;
  4969. {
  4970.     dprint(2, (debugfile,
  4971.        "adrbk_add: entry marked inserted, count was %lu, now %lu\n",
  4972.        (unsigned long)ab->count, (unsigned long)(ab->count + 1)));
  4973.  
  4974.     ab->count++;
  4975.     insert_before              = (adrbk_cntr_t)put_it_before_this;
  4976.     inserted_entryref.uid_nick = !NO_UID;
  4977.     inserted_entryref.ae       = new_ae;
  4978. }
  4979.  
  4980. /*
  4981.  * Moves element move_this_one before element put_it_before_this.
  4982.  */
  4983. void
  4984. set_moved_entryref(move_this_one, put_it_before_this)
  4985.     a_c_arg_t move_this_one;
  4986.     a_c_arg_t put_it_before_this;
  4987. {
  4988.     dprint(7, (debugfile, "- set_moved_entryref -\n"));
  4989.  
  4990.     moved_elem       = (adrbk_cntr_t)move_this_one;
  4991.     move_elem_before = (adrbk_cntr_t)put_it_before_this;
  4992. }
  4993.  
  4994.  
  4995. void
  4996. init_entryref_cache(ab)
  4997.     AdrBk *ab;
  4998. {
  4999.     adrbk_cntr_t i;
  5000.     adrbk_cntr_t new_hashsize;
  5001.  
  5002.     dprint(9, (debugfile, "- init_entryref_cache -\n"));
  5003.  
  5004.     if(ab->er_hashsize != 0) /* an indication we've init'd before */
  5005.       clear_entryref_cache(ab);
  5006.  
  5007.     new_hashsize = min((adrbk_cntr_t)(ab->nominal_max_cached /
  5008.                             CACHE_PER_ER_BUCKET),
  5009.                (adrbk_cntr_t)30000);
  5010.  
  5011.     if(new_hashsize == 0)
  5012.       new_hashsize = 1;
  5013.  
  5014.     if(new_hashsize != ab->er_hashsize){
  5015.  
  5016.     ab->er_hashsize = new_hashsize;
  5017.  
  5018.     /* hash array of head pointers */
  5019.     if(ab->head_cache_elem)
  5020.       fs_give((void **)&ab->head_cache_elem);
  5021.  
  5022.     if(ab->tail_cache_elem)
  5023.       fs_give((void **)&ab->tail_cache_elem);
  5024.  
  5025.     if(ab->n_ae_cached_in_this_bucket)
  5026.       fs_give((void **)&ab->n_ae_cached_in_this_bucket);
  5027.  
  5028.     ab->head_cache_elem =
  5029.       (ER_CACHE_ELEM_S **)fs_get((size_t)ab->er_hashsize *
  5030.                       sizeof(ER_CACHE_ELEM_S *));
  5031.     ab->tail_cache_elem =
  5032.       (ER_CACHE_ELEM_S **)fs_get((size_t)ab->er_hashsize *
  5033.                       sizeof(ER_CACHE_ELEM_S *));
  5034.     ab->n_ae_cached_in_this_bucket =
  5035.       (int *)fs_get((size_t)ab->er_hashsize * sizeof(int));
  5036.     memset(ab->n_ae_cached_in_this_bucket, 0,
  5037.         (size_t)ab->er_hashsize * sizeof(int));
  5038.     }
  5039.  
  5040.     for(i = 0; i < ab->er_hashsize; i++){
  5041.     ab->head_cache_elem[i] = (ER_CACHE_ELEM_S *)NULL;
  5042.     ab->tail_cache_elem[i] = (ER_CACHE_ELEM_S *)NULL;
  5043.     }
  5044.  
  5045.     deleted_elem     = NO_NEXT;
  5046.     deleted_ae       = NULL;
  5047.     moved_elem       = NO_NEXT;
  5048.     move_elem_before = NO_NEXT;
  5049.     if(insert_before != NO_NEXT){
  5050.     insert_before    = NO_NEXT;
  5051.     if(inserted_entryref.ae != (AdrBk_Entry *)NULL){
  5052.         free_ae(ab, &(inserted_entryref.ae));
  5053.         inserted_entryref.ae = (AdrBk_Entry *)NULL;
  5054.     }
  5055.     }
  5056. }
  5057.  
  5058.  
  5059. /*
  5060.  * Clear the entire cache.
  5061.  */
  5062. void
  5063. clear_entryref_cache(ab)
  5064.     AdrBk *ab;
  5065. {
  5066.     ER_CACHE_ELEM_S *cptr, *next;
  5067.     adrbk_cntr_t i;
  5068.  
  5069.     dprint(9, (debugfile, "- clear_entryref_cache -\n"));
  5070.  
  5071.     for(i = 0; i < ab->er_hashsize; i++){
  5072.     ab->n_ae_cached_in_this_bucket[i] = 0;
  5073.     cptr = ab->head_cache_elem[i];
  5074.     while(cptr){
  5075.         free_ab_entryref(ab, cptr->entry);
  5076.         next = cptr->next;
  5077.         fs_give((void **)&cptr);
  5078.         cptr = next;
  5079.     }
  5080.     }
  5081. }
  5082.  
  5083.  
  5084. void
  5085. clearrefs_in_cached_aes(ab)
  5086.     AdrBk *ab;
  5087. {
  5088.     ER_CACHE_ELEM_S *cptr;
  5089.     adrbk_cntr_t i;
  5090.  
  5091.     for(i = 0; i < ab->er_hashsize; i++){
  5092.     cptr = ab->head_cache_elem[i];
  5093.     while(cptr){
  5094.         if(cptr->entry && cptr->entry->ae)
  5095.           cptr->entry->ae->referenced = 0;
  5096.  
  5097.         cptr = cptr->next;
  5098.     }
  5099.     }
  5100. }
  5101.  
  5102.  
  5103. /*
  5104.  * Free the list of distribution lists which have been expanded.
  5105.  * Leaves the head of the list alone.
  5106.  *
  5107.  * Args:  exp_head -- Head of the expanded list.
  5108.  */
  5109. void
  5110. exp_free(exp_head)
  5111.     EXPANDED_S *exp_head;
  5112. {
  5113.     EXPANDED_S *e, *the_next_one;
  5114.  
  5115.     e = exp_head ? exp_head->next : NULL;
  5116.  
  5117.     if(!e)
  5118.       return;
  5119.  
  5120.     while(e){
  5121.     the_next_one = e->next;
  5122.     fs_give((void **)&e);
  5123.     e = the_next_one;
  5124.     }
  5125.  
  5126.     exp_head->next = (EXPANDED_S *)NULL;
  5127. }
  5128.  
  5129.  
  5130. /*
  5131.  * Is entry n expanded?
  5132.  *
  5133.  * Args:  exp_head -- Head of the expanded list.
  5134.  *        n        -- The entry num to check
  5135.  */
  5136. int
  5137. exp_is_expanded(exp_head, n)
  5138.     EXPANDED_S *exp_head;
  5139.     a_c_arg_t   n;
  5140. {
  5141.     register EXPANDED_S *e;
  5142.     adrbk_cntr_t nn;
  5143.  
  5144.     nn = (adrbk_cntr_t)n;
  5145.  
  5146.     /*
  5147.      * The list is kept ordered, so we search until we find it or are
  5148.      * past it.
  5149.      */
  5150.     for(e = exp_head->next; e; e = e->next)
  5151.       if(e->ent >= nn)
  5152.     break;
  5153.     
  5154.     return(e && e->ent == nn);
  5155. }
  5156.  
  5157.  
  5158. /*
  5159.  * Return next entry num in list.
  5160.  *
  5161.  * Args:  cur -- Current position in the list.
  5162.  *
  5163.  * Result: Returns the number of the next entry, or NO_NEXT if there is
  5164.  *       no next entry.  As a side effect, the cur pointer is incremented.
  5165.  */
  5166. adrbk_cntr_t
  5167. exp_get_next(cur)
  5168.     EXPANDED_S **cur;
  5169. {
  5170.     adrbk_cntr_t ret = NO_NEXT;
  5171.  
  5172.     if(cur && *cur && (*cur)->next){
  5173.     ret  = (*cur)->next->ent;
  5174.     *cur = (*cur)->next;
  5175.     }
  5176.  
  5177.     return(ret);
  5178. }
  5179.  
  5180.  
  5181. /*
  5182.  * Mark entry n as being expanded.
  5183.  *
  5184.  * Args:  exp_head -- Head of the expanded list.
  5185.  *        n        -- The entry num to mark
  5186.  */
  5187. void
  5188. exp_set_expanded(exp_head, n)
  5189.     EXPANDED_S *exp_head;
  5190.     a_c_arg_t   n;
  5191. {
  5192.     register EXPANDED_S *e;
  5193.     EXPANDED_S *new;
  5194.     adrbk_cntr_t nn;
  5195.  
  5196.     nn = (adrbk_cntr_t)n;
  5197.  
  5198.     for(e = exp_head; e->next; e = e->next)
  5199.       if(e->next->ent >= nn)
  5200.     break;
  5201.     
  5202.     if(e->next && e->next->ent == nn) /* already there */
  5203.       return;
  5204.  
  5205.     /* add new after e */
  5206.     new       = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
  5207.     new->ent  = nn;
  5208.     new->next = e->next;
  5209.     e->next   = new;
  5210. }
  5211.  
  5212.  
  5213. /*
  5214.  * Mark entry n as being *not* expanded.
  5215.  *
  5216.  * Args:  exp_head -- Head of the expanded list.
  5217.  *        n        -- The entry num to mark
  5218.  */
  5219. void
  5220. exp_unset_expanded(exp_head, n)
  5221.     EXPANDED_S *exp_head;
  5222.     a_c_arg_t   n;
  5223. {
  5224.     register EXPANDED_S *e;
  5225.     EXPANDED_S *delete_this_one;
  5226.     adrbk_cntr_t nn;
  5227.  
  5228.     nn = (adrbk_cntr_t)n;
  5229.  
  5230.     for(e = exp_head; e->next; e = e->next)
  5231.       if(e->next->ent >= nn)
  5232.     break;
  5233.     
  5234.     if(e->next && e->next->ent == nn){
  5235.     delete_this_one = e->next;
  5236.     e->next = e->next->next;
  5237.     }
  5238.  
  5239.     fs_give((void **)&delete_this_one);
  5240. }
  5241.  
  5242.  
  5243. /*
  5244.  * Adjust the "expanded" list to correspond to addrbook entry n being
  5245.  * deleted.
  5246.  *
  5247.  * Args:  exp_head -- Head of the expanded list.
  5248.  *        n        -- The entry num being deleted
  5249.  */
  5250. void
  5251. exp_del_nth(exp_head, n)
  5252.     EXPANDED_S *exp_head;
  5253.     a_c_arg_t   n;
  5254. {
  5255.     register EXPANDED_S *e;
  5256.     int delete_when_done = 0;
  5257.     adrbk_cntr_t nn;
  5258.  
  5259.     nn = (adrbk_cntr_t)n;
  5260.  
  5261.     e = exp_head->next;
  5262.     while(e && e->ent < nn)
  5263.       e = e->next;
  5264.     
  5265.     if(e){
  5266.     if(e->ent == nn){
  5267.         delete_when_done++;
  5268.         e = e->next;
  5269.     }
  5270.  
  5271.     while(e){
  5272.         e->ent--; /* adjust entry nums */
  5273.         e = e->next;
  5274.     }
  5275.  
  5276.     if(delete_when_done)
  5277.       exp_unset_expanded(exp_head, n);
  5278.     }
  5279. }
  5280.  
  5281.  
  5282. /*
  5283.  * Adjust the "expanded" list to correspond to a new addrbook entry being
  5284.  * added between current entries n-1 and n.
  5285.  *
  5286.  * Args:  exp_head -- Head of the expanded list.
  5287.  *        n        -- The entry num being added
  5288.  *
  5289.  * The new entry is not marked expanded.
  5290.  */
  5291. void
  5292. exp_add_nth(exp_head, n)
  5293.     EXPANDED_S *exp_head;
  5294.     a_c_arg_t   n;
  5295. {
  5296.     register EXPANDED_S *e;
  5297.     adrbk_cntr_t nn;
  5298.  
  5299.     nn = (adrbk_cntr_t)n;
  5300.  
  5301.     e = exp_head->next;
  5302.     while(e && e->ent < nn)
  5303.       e = e->next;
  5304.     
  5305.     while(e){
  5306.     e->ent++; /* adjust entry nums */
  5307.     e = e->next;
  5308.     }
  5309. }
  5310.  
  5311.  
  5312. static AdrBk *ab_for_sort;
  5313.  
  5314. /*
  5315.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5316.  * Sorts lists after simple addresses and then sorts on Fullname field.
  5317.  */
  5318. int
  5319. cmp_ae_by_full_lists_last(a, b)
  5320.     const QSType *a,
  5321.          *b;
  5322. {
  5323.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5324.                 **y = (AdrBk_Entry **)b;
  5325.     int result;
  5326.  
  5327.     if((*x)->tag == List && (*y)->tag == Single)
  5328.       result = 1;
  5329.     else if((*x)->tag == Single && (*y)->tag == List)
  5330.       result = -1;
  5331.     else{
  5332.     register char *p, *q;
  5333.  
  5334.     p = (*x)->fullname;
  5335.     if(*p == '"' && *(p+1))
  5336.       p++;
  5337.  
  5338.     q = (*y)->fullname;
  5339.     if(*q == '"' && *(q+1))
  5340.       q++;
  5341.  
  5342.     result = strucmp(p, q);
  5343.     if(result == 0)
  5344.       result = strucmp((*x)->nickname, (*y)->nickname);
  5345.     }
  5346.       
  5347.     return(result);
  5348. }
  5349.  
  5350.  
  5351. /*
  5352.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5353.  * Sorts lists after simple addresses and then sorts on Fullname field.
  5354.  */
  5355. int
  5356. cmp_cntr_by_full_lists_last(a, b)
  5357.     const QSType *a,
  5358.          *b;
  5359. {
  5360.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5361.                  *y = (adrbk_cntr_t *)b;
  5362.     AdrBk_Entry  *x_ae,
  5363.          *y_ae;
  5364.  
  5365.     if(ps_global->intr_pending)
  5366.       longjmp(jump_over_qsort, 1);
  5367.  
  5368.     ALARM_BLIP();
  5369.  
  5370.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5371.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5372.  
  5373.     return(cmp_ae_by_full_lists_last((const QSType *) &x_ae,
  5374.                      (const QSType *) &y_ae));
  5375. }
  5376.  
  5377.  
  5378. /*
  5379.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5380.  * Sorts on Fullname field.
  5381.  */
  5382. int
  5383. cmp_ae_by_full(a, b)
  5384.     const QSType *a,
  5385.          *b;
  5386. {
  5387.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5388.                 **y = (AdrBk_Entry **)b;
  5389.     int result;
  5390.  
  5391.     result = strucmp((*x)->fullname, (*y)->fullname);
  5392.     if(result == 0)
  5393.       result = strucmp((*x)->nickname, (*y)->nickname);
  5394.       
  5395.     return(result);
  5396. }
  5397.  
  5398.  
  5399. /*
  5400.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5401.  * Sorts on Fullname field.
  5402.  */
  5403. int
  5404. cmp_cntr_by_full(a, b)
  5405.     const QSType *a,
  5406.          *b;
  5407. {
  5408.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5409.                  *y = (adrbk_cntr_t *)b;
  5410.     AdrBk_Entry  *x_ae,
  5411.          *y_ae;
  5412.  
  5413.     if(ps_global->intr_pending)
  5414.       longjmp(jump_over_qsort, 1);
  5415.  
  5416.     ALARM_BLIP();
  5417.  
  5418.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5419.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5420.  
  5421.     return(cmp_ae_by_full((const QSType *) &x_ae, (const QSType *) &y_ae));
  5422. }
  5423.  
  5424.  
  5425. /*
  5426.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5427.  * Sorts lists after simple addresses and then sorts on Nickname field.
  5428.  */
  5429. int
  5430. cmp_ae_by_nick_lists_last(a, b)
  5431.     const QSType *a,
  5432.          *b;
  5433. {
  5434.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5435.                 **y = (AdrBk_Entry **)b;
  5436.     int result;
  5437.  
  5438.     if((*x)->tag == List && (*y)->tag == Single)
  5439.       result = 1;
  5440.     else if((*x)->tag == Single && (*y)->tag == List)
  5441.       result = -1;
  5442.     else
  5443.       result = strucmp((*x)->nickname, (*y)->nickname);
  5444.  
  5445.     return(result);
  5446. }
  5447.  
  5448.  
  5449. /*
  5450.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5451.  * Sorts lists after simple addresses and then sorts on Nickname field.
  5452.  */
  5453. int
  5454. cmp_cntr_by_nick_lists_last(a, b)
  5455.     const QSType *a,
  5456.          *b;
  5457. {
  5458.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5459.                  *y = (adrbk_cntr_t *)b;
  5460.     AdrBk_Entry  *x_ae,
  5461.          *y_ae;
  5462.  
  5463.     if(ps_global->intr_pending)
  5464.       longjmp(jump_over_qsort, 1);
  5465.  
  5466.     ALARM_BLIP();
  5467.  
  5468.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5469.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5470.  
  5471.     return(cmp_ae_by_nick_lists_last((const QSType *) &x_ae,
  5472.                      (const QSType *) &y_ae));
  5473. }
  5474.  
  5475.  
  5476. /*
  5477.  * Compare two address book entries.  Args are AdrBk_Entry **'s.
  5478.  * Sorts on Nickname field.
  5479.  */
  5480. int
  5481. cmp_ae_by_nick(a, b)
  5482.     const QSType *a,
  5483.          *b;
  5484. {
  5485.     AdrBk_Entry **x = (AdrBk_Entry **)a,
  5486.                 **y = (AdrBk_Entry **)b;
  5487.  
  5488.     return(strucmp((*x)->nickname, (*y)->nickname));
  5489. }
  5490.  
  5491.  
  5492. /*
  5493.  * Compare two address book entries.  Args are adrbk_cntr_t *'s (element #'s).
  5494.  * Sorts on Nickname field.
  5495.  */
  5496. int
  5497. cmp_cntr_by_nick(a, b)
  5498.     const QSType *a,
  5499.          *b;
  5500. {
  5501.     adrbk_cntr_t *x = (adrbk_cntr_t *)a,  /* *x is an element_number */
  5502.                  *y = (adrbk_cntr_t *)b;
  5503.     AdrBk_Entry  *x_ae,
  5504.          *y_ae;
  5505.  
  5506.     if(ps_global->intr_pending)
  5507.       longjmp(jump_over_qsort, 1);
  5508.  
  5509.     ALARM_BLIP();
  5510.  
  5511.     x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x), Normal);
  5512.     y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y), Normal);
  5513.  
  5514.     return(cmp_ae_by_nick((const QSType *) &x_ae, (const QSType *) &y_ae));
  5515. }
  5516.  
  5517.  
  5518. /*
  5519.  * For sorting a simple list of pointers to addresses (skip initial quotes)
  5520.  */
  5521. int
  5522. cmp_addr(a1, a2)
  5523.     const QSType *a1, *a2;
  5524. {
  5525.     char *x = *(char **)a1, *y = *(char **)a2;
  5526.  
  5527.     if(x && *x == '"')
  5528.       x++;
  5529.  
  5530.     if(y && *y == '"')
  5531.       y++;
  5532.  
  5533.     return(strucmp(x, y));
  5534. }
  5535.  
  5536.  
  5537. /*
  5538.  * Sort an array of strings, except skip initial quotes.
  5539.  */
  5540. void
  5541. sort_addr_list(list)
  5542.     char **list;
  5543. {
  5544.     register char **p;
  5545.  
  5546.     /* find size of list */
  5547.     for(p = list; *p != NULL; p++)
  5548.       ;/* do nothing */
  5549.  
  5550.     qsort((QSType *)list,
  5551. #ifdef DYN
  5552.           (p - list),
  5553. #else          
  5554.           (size_t)(p - list),
  5555. #endif          
  5556.           sizeof(char *), cmp_addr);
  5557. }
  5558.  
  5559.  
  5560. /*
  5561.  * Sort this address book.
  5562.  *
  5563.  * Args: ab            -- address book to sort
  5564.  *   current_entry_num -- see next description
  5565.  *   new_entry_num     -- return new entry_num of current_entry_num here
  5566.  *
  5567.  * Result: return code:  0 all went well
  5568.  *                      -2 error writing address book, check errno
  5569.  *
  5570.  * The sorting strategy is to allocate an array of length ab->count which
  5571.  * contains the element numbers 0, 1, ..., ab->count - 1, representing the
  5572.  * entries in the addrbook, of course.  Sort the array, then write it out in
  5573.  * the new order and start over from there.
  5574.  */
  5575. int
  5576. adrbk_sort(ab, current_entry_num, new_entry_num, be_quiet)
  5577.     AdrBk        *ab;
  5578.     a_c_arg_t     current_entry_num;
  5579.     adrbk_cntr_t *new_entry_num;
  5580.     int           be_quiet;
  5581. {
  5582.     adrbk_cntr_t *sort_array;
  5583.     long i;
  5584.     long count;
  5585.     int result;
  5586.     int skip_the_sort = 0;
  5587.     int we_cancel = 0;
  5588.  
  5589.     dprint(7, (debugfile, "- adrbk_sort -\n"));
  5590.  
  5591.     count = (long)(ab->count);
  5592.  
  5593.     if(!ab)
  5594.       return -2;
  5595.  
  5596.     if(ab->sort_rule == AB_SORT_RULE_NONE)
  5597.       return 0;
  5598.     
  5599.     if(count < 2)
  5600.       return 0;
  5601.  
  5602.     if(!be_quiet)
  5603.       we_cancel = busy_alarm(1, "Sorting address book", NULL, 1);
  5604.  
  5605.     sort_array = (adrbk_cntr_t *)fs_get((size_t)count * sizeof(adrbk_cntr_t));
  5606.     
  5607.     for(i = 0L; i < count; i++)
  5608.       sort_array[i] = (adrbk_cntr_t)i;
  5609.     
  5610.     ab_for_sort = ab;
  5611.  
  5612.  
  5613.     if(setjmp(jump_over_qsort))
  5614.       skip_the_sort = 1;
  5615.  
  5616.     if(!skip_the_sort){
  5617.     intr_handling_on();
  5618.     qsort((QSType *)sort_array,
  5619.         (size_t)count,
  5620.         sizeof(adrbk_cntr_t),
  5621.         (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
  5622.                         cmp_cntr_by_full_lists_last :
  5623.         (ab->sort_rule == AB_SORT_RULE_FULL) ?
  5624.                         cmp_cntr_by_full :
  5625.         (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
  5626.                         cmp_cntr_by_nick_lists_last :
  5627.         /* (ab->sort_rule == AB_SORT_RULE_NICK) */
  5628.                         cmp_cntr_by_nick);
  5629.     }
  5630.  
  5631.     intr_handling_off();
  5632.     if(we_cancel)
  5633.       cancel_busy_alarm(0);
  5634.  
  5635.     if(skip_the_sort){
  5636.     q_status_message(SM_ORDER, 3, 3,
  5637.         "Address book sort cancelled, using old order for now");
  5638.     goto skip_the_write_too;
  5639.     }
  5640.  
  5641.     dprint(2,
  5642.         (debugfile, "- adrbk_sort (%s)\n",
  5643.       ab->sort_rule==AB_SORT_RULE_FULL_LISTS ? "FullListsLast" :
  5644.        ab->sort_rule==AB_SORT_RULE_FULL ? "Fullname" :
  5645.         ab->sort_rule==AB_SORT_RULE_NICK_LISTS ? "NickListLast" :
  5646.          ab->sort_rule==AB_SORT_RULE_NICK ? "Nickname" : "unknown"));
  5647.  
  5648.     result = adrbk_write(ab, sort_array, be_quiet);
  5649.  
  5650.     if(result == 0)
  5651.       exp_free(ab->exp);
  5652.     else if(result == -2)
  5653.       q_status_message(SM_ORDER, 3, 4, "address book sort failed, can't save");
  5654.  
  5655.     /*
  5656.      * Look through the sort_array to find where current_entry_num moved to.
  5657.      */
  5658.     if(result == 0 && new_entry_num){
  5659.     for(i = 0L; i < count; i++)
  5660.       if((adrbk_cntr_t)current_entry_num == sort_array[i]){
  5661.           *new_entry_num = (adrbk_cntr_t)i;
  5662.           break;
  5663.       }
  5664.     }
  5665.  
  5666. skip_the_write_too:
  5667.     fs_give((void **)&sort_array);
  5668.  
  5669.     return(result);
  5670. }
  5671.